1 /* 2 * Copyright 2009, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2013 Haiku, Inc. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 /* 8 * Copyright (c) 2002-2004, Marcus Overhagen <marcus@overhagen.de> 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without modification, 12 * are permitted provided that the following conditions are met: 13 * 14 * * Redistributions of source code must retain the above copyright notice, 15 * this list of conditions and the following disclaimer. 16 * * Redistributions in binary form must reproduce the above copyright notice, 17 * this list of conditions and the following disclaimer in the documentation 18 * and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 33 #include <map> 34 #include <stdio.h> 35 #include <vector> 36 37 #include <Alert.h> 38 #include <Application.h> 39 #include <Beep.h> 40 #include <Directory.h> 41 #include <Entry.h> 42 #include <MediaAddOn.h> 43 #include <MediaRoster.h> 44 #include <MessageRunner.h> 45 #include <Path.h> 46 #include <Roster.h> 47 #include <String.h> 48 49 #include <AddOnMonitorHandler.h> 50 #include <DataExchange.h> 51 #include <DormantNodeManager.h> 52 #include <MediaDebug.h> 53 #include <MediaMisc.h> 54 #include <MediaRosterEx.h> 55 #include <MediaSounds.h> 56 #include <Notifications.h> 57 #include <ServerInterface.h> 58 59 #include "MediaFilePlayer.h" 60 #include "SystemTimeSource.h" 61 62 63 //#define USER_ADDON_PATH "../add-ons/media" 64 65 66 typedef std::vector<media_node> NodeVector; 67 68 69 struct AddOnInfo { 70 media_addon_id id; 71 bool wants_autostart; 72 int32 flavor_count; 73 74 NodeVector active_flavors; 75 76 // if != NULL, need to call gDormantNodeManager->PutAddOn(id) 77 BMediaAddOn* addon; 78 }; 79 80 81 class MediaAddonServer : BApplication { 82 public: 83 MediaAddonServer(const char* signature); 84 virtual ~MediaAddonServer(); 85 virtual void ReadyToRun(); 86 virtual bool QuitRequested(); 87 virtual void MessageReceived(BMessage* message); 88 89 private: 90 class MonitorHandler; 91 friend class MonitorHandler; 92 93 void _AddOnAdded(const char* path, ino_t fileNode); 94 void _AddOnRemoved(ino_t fileNode); 95 void _HandleMessage(int32 code, const void* data, 96 size_t size); 97 98 void _PutAddonIfPossible(AddOnInfo& info); 99 void _InstantiatePhysicalInputsAndOutputs( 100 AddOnInfo& info); 101 void _InstantiateAutostartFlavors(AddOnInfo& info); 102 void _DestroyInstantiatedFlavors(AddOnInfo& info); 103 104 void _ScanAddOnFlavors(BMediaAddOn* addOn); 105 106 port_id _ControlPort() const { return fControlPort; } 107 108 static status_t _ControlThread(void* arg); 109 110 private: 111 typedef std::map<ino_t, media_addon_id> FileMap; 112 typedef std::map<media_addon_id, AddOnInfo> InfoMap; 113 114 FileMap fFileMap; 115 InfoMap fInfoMap; 116 117 BMediaRoster* fMediaRoster; 118 MonitorHandler* fMonitorHandler; 119 BMessageRunner* fPulseRunner; 120 port_id fControlPort; 121 thread_id fControlThread; 122 bool fStartup; 123 bool fStartupSound; 124 SystemTimeSource* fSystemTimeSource; 125 }; 126 127 128 class MediaAddonServer::MonitorHandler : public AddOnMonitorHandler { 129 public: 130 MonitorHandler(MediaAddonServer* server); 131 132 virtual void AddOnEnabled(const add_on_entry_info* info); 133 virtual void AddOnDisabled(const add_on_entry_info* info); 134 135 private: 136 MediaAddonServer* fServer; 137 }; 138 139 140 #if DEBUG >= 2 141 static void 142 DumpFlavorInfo(const flavor_info* info) 143 { 144 printf(" name = %s\n", info->name); 145 printf(" info = %s\n", info->info); 146 printf(" internal_id = %" B_PRId32 "\n", info->internal_id); 147 printf(" possible_count = %" B_PRId32 "\n", info->possible_count); 148 printf(" flavor_flags = 0x%" B_PRIx32, info->flavor_flags); 149 if (info->flavor_flags & B_FLAVOR_IS_GLOBAL) printf(" B_FLAVOR_IS_GLOBAL"); 150 if (info->flavor_flags & B_FLAVOR_IS_LOCAL) printf(" B_FLAVOR_IS_LOCAL"); 151 printf("\n"); 152 printf(" kinds = 0x%" B_PRIx64, info->kinds); 153 if (info->kinds & B_BUFFER_PRODUCER) printf(" B_BUFFER_PRODUCER"); 154 if (info->kinds & B_BUFFER_CONSUMER) printf(" B_BUFFER_CONSUMER"); 155 if (info->kinds & B_TIME_SOURCE) printf(" B_TIME_SOURCE"); 156 if (info->kinds & B_CONTROLLABLE) printf(" B_CONTROLLABLE"); 157 if (info->kinds & B_FILE_INTERFACE) printf(" B_FILE_INTERFACE"); 158 if (info->kinds & B_ENTITY_INTERFACE) printf(" B_ENTITY_INTERFACE"); 159 if (info->kinds & B_PHYSICAL_INPUT) printf(" B_PHYSICAL_INPUT"); 160 if (info->kinds & B_PHYSICAL_OUTPUT) printf(" B_PHYSICAL_OUTPUT"); 161 if (info->kinds & B_SYSTEM_MIXER) printf(" B_SYSTEM_MIXER"); 162 printf("\n"); 163 printf(" in_format_count = %" B_PRId32 "\n", info->in_format_count); 164 printf(" out_format_count = %" B_PRId32 "\n", info->out_format_count); 165 } 166 #endif 167 168 169 // #pragma mark - 170 171 172 MediaAddonServer::MonitorHandler::MonitorHandler(MediaAddonServer* server) 173 { 174 fServer = server; 175 } 176 177 178 void 179 MediaAddonServer::MonitorHandler::AddOnEnabled(const add_on_entry_info* info) 180 { 181 entry_ref ref; 182 make_entry_ref(info->dir_nref.device, info->dir_nref.node, 183 info->name, &ref); 184 BEntry entry(&ref, true); 185 if (!entry.IsFile()) 186 return; 187 188 BPath path(&ref); 189 if (path.InitCheck() == B_OK) 190 fServer->_AddOnAdded(path.Path(), info->nref.node); 191 } 192 193 194 void 195 MediaAddonServer::MonitorHandler::AddOnDisabled(const add_on_entry_info* info) 196 { 197 fServer->_AddOnRemoved(info->nref.node); 198 } 199 200 201 // #pragma mark - 202 203 204 MediaAddonServer::MediaAddonServer(const char* signature) 205 : 206 BApplication(signature), 207 fMonitorHandler(NULL), 208 fPulseRunner(NULL), 209 fStartup(true), 210 fStartupSound(true), 211 fSystemTimeSource(NULL) 212 { 213 CALLED(); 214 fMediaRoster = BMediaRoster::Roster(); 215 fControlPort = create_port(64, MEDIA_ADDON_SERVER_PORT_NAME); 216 fControlThread = spawn_thread(_ControlThread, "media_addon_server control", 217 B_NORMAL_PRIORITY + 2, this); 218 resume_thread(fControlThread); 219 } 220 221 222 MediaAddonServer::~MediaAddonServer() 223 { 224 CALLED(); 225 226 delete_port(fControlPort); 227 wait_for_thread(fControlThread, NULL); 228 229 // unregister all media add-ons 230 FileMap::iterator iterator = fFileMap.begin(); 231 for (; iterator != fFileMap.end(); iterator++) 232 gDormantNodeManager->UnregisterAddOn(iterator->second); 233 234 delete fMonitorHandler; 235 delete fPulseRunner; 236 } 237 238 239 void 240 MediaAddonServer::ReadyToRun() 241 { 242 if (!be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)) { 243 // the media server is not running, let's quit 244 fprintf(stderr, "The media_server is not running!\n"); 245 Quit(); 246 return; 247 } 248 249 // the control thread is already running at this point, 250 // so we can talk to the media server and also receive 251 // commands for instantiation 252 253 ASSERT(fStartup == true); 254 255 // The very first thing to do is to create the system time source, 256 // register it with the server, and make it the default SYSTEM_TIME_SOURCE 257 fSystemTimeSource = new SystemTimeSource(); 258 status_t result = fMediaRoster->RegisterNode(fSystemTimeSource); 259 if (result != B_OK) { 260 fprintf(stderr, "Can't register system time source : %s\n", 261 strerror(result)); 262 ERROR("Can't register system time source"); 263 } 264 265 if (fSystemTimeSource->ID() != NODE_SYSTEM_TIMESOURCE_ID) 266 ERROR("System time source got wrong node ID"); 267 media_node node = fSystemTimeSource->Node(); 268 result = MediaRosterEx(fMediaRoster)->SetNode(SYSTEM_TIME_SOURCE, &node); 269 if (result != B_OK) 270 ERROR("Can't setup system time source as default"); 271 272 // During startup, first all add-ons are loaded, then all 273 // nodes (flavors) representing physical inputs and outputs 274 // are instantiated. Next, all add-ons that need autostart 275 // will be autostarted. Finally, add-ons that don't have 276 // any active nodes (flavors) will be unloaded. 277 278 fMonitorHandler = new MonitorHandler(this); 279 AddHandler(fMonitorHandler); 280 281 BMessage pulse(B_PULSE); 282 // the monitor handler needs a pulse to check if add-ons are ready 283 fPulseRunner = new BMessageRunner(fMonitorHandler, &pulse, 1000000LL); 284 285 result = fPulseRunner->InitCheck(); 286 if (result != B_OK) 287 ERROR("Can't create the pulse runner"); 288 289 fMonitorHandler->AddAddOnDirectories("media"); 290 291 #ifdef USER_ADDON_PATH 292 node_ref nodeRef; 293 if (entry.SetTo(USER_ADDON_PATH) == B_OK 294 && entry.GetNodeRef(&nodeRef) == B_OK) { 295 fMonitorHandler->AddDirectory(&nodeRef); 296 } 297 #endif 298 299 fStartup = false; 300 301 InfoMap::iterator iterator = fInfoMap.begin(); 302 for (; iterator != fInfoMap.end(); iterator++) 303 _InstantiatePhysicalInputsAndOutputs(iterator->second); 304 305 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 306 _InstantiateAutostartFlavors(iterator->second); 307 308 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 309 _PutAddonIfPossible(iterator->second); 310 311 server_rescan_defaults_command cmd; 312 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 313 } 314 315 316 bool 317 MediaAddonServer::QuitRequested() 318 { 319 CALLED(); 320 321 InfoMap::iterator iterator = fInfoMap.begin(); 322 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 323 _DestroyInstantiatedFlavors(iterator->second); 324 325 // the System timesource should be removed before we quit the roster 326 if (fSystemTimeSource != NULL && 327 be_roster->IsRunning(B_MEDIA_SERVER_SIGNATURE)) { 328 status_t result = fMediaRoster->UnregisterNode(fSystemTimeSource); 329 if (result != B_OK) { 330 fprintf(stderr, "Error removing the system time source : %s\n", 331 strerror(result)); 332 ERROR("Can't remove the system time source"); 333 } 334 fSystemTimeSource->Release(); 335 fSystemTimeSource = NULL; 336 } 337 338 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 339 _PutAddonIfPossible(iterator->second); 340 341 return true; 342 } 343 344 345 void 346 MediaAddonServer::MessageReceived(BMessage* message) 347 { 348 switch (message->what) { 349 case MEDIA_ADD_ON_SERVER_PLAY_MEDIA: 350 { 351 const char* name; 352 const char* type; 353 if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK 354 || message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) { 355 message->SendReply(B_ERROR); 356 break; 357 } 358 359 PlayMediaFile(type, name); 360 message->SendReply((uint32)B_OK); 361 // TODO: don't know which reply is expected 362 return; 363 } 364 365 default: 366 BApplication::MessageReceived(message); 367 break; 368 } 369 } 370 371 372 void 373 MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size) 374 { 375 switch (code) { 376 case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE: 377 { 378 const add_on_server_instantiate_dormant_node_request* request 379 = static_cast< 380 const add_on_server_instantiate_dormant_node_request*>( 381 data); 382 add_on_server_instantiate_dormant_node_reply reply; 383 384 status_t status 385 = MediaRosterEx(fMediaRoster)->InstantiateDormantNode( 386 request->add_on_id, request->flavor_id, 387 request->creator_team, &reply.node); 388 request->SendReply(status, &reply, sizeof(reply)); 389 break; 390 } 391 392 case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS: 393 { 394 const add_on_server_rescan_flavors_command* command = static_cast< 395 const add_on_server_rescan_flavors_command*>(data); 396 BMediaAddOn* addOn 397 = gDormantNodeManager->GetAddOn(command->add_on_id); 398 if (addOn == NULL) { 399 ERROR("rescan flavors: Can't find a addon object for id %d\n", 400 (int)command->add_on_id); 401 break; 402 } 403 _ScanAddOnFlavors(addOn); 404 gDormantNodeManager->PutAddOn(command->add_on_id); 405 break; 406 } 407 408 case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY: 409 if (fStartupSound) { 410 BMessage msg(MEDIA_ADD_ON_SERVER_PLAY_MEDIA); 411 msg.AddString(MEDIA_NAME_KEY, MEDIA_SOUNDS_STARTUP); 412 msg.AddString(MEDIA_TYPE_KEY, MEDIA_TYPE_SOUNDS); 413 BMessageRunner::StartSending(this, &msg, 2000000, 1); 414 fStartupSound = false; 415 } 416 break; 417 418 default: 419 ERROR("media_addon_server: received unknown message code %#08" 420 B_PRIx32 "\n", code); 421 break; 422 } 423 } 424 425 426 status_t 427 MediaAddonServer::_ControlThread(void* _server) 428 { 429 MediaAddonServer* server = (MediaAddonServer*)_server; 430 431 char data[B_MEDIA_MESSAGE_SIZE]; 432 ssize_t size; 433 int32 code; 434 while ((size = read_port_etc(server->_ControlPort(), &code, data, 435 sizeof(data), 0, 0)) > 0) 436 server->_HandleMessage(code, data, size); 437 438 return B_OK; 439 } 440 441 442 void 443 MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon) 444 { 445 ASSERT(addon->AddonID() > 0); 446 447 TRACE("MediaAddonServer::_ScanAddOnFlavors: id %" B_PRId32 "\n", 448 addon->AddonID()); 449 450 // cache the media_addon_id in a local variable to avoid 451 // calling BMediaAddOn::AddonID() too often 452 media_addon_id addonID = addon->AddonID(); 453 454 // update the cached flavor count, get oldflavorcount and newflavorcount 455 456 InfoMap::iterator found = fInfoMap.find(addonID); 457 ASSERT(found != fInfoMap.end()); 458 459 AddOnInfo& info = found->second; 460 int32 oldFlavorCount = info.flavor_count; 461 int32 newFlavorCount = addon->CountFlavors(); 462 info.flavor_count = newFlavorCount; 463 464 TRACE("%" B_PRId32 " old flavors, %" B_PRId32 " new flavors\n", 465 oldFlavorCount, newFlavorCount); 466 467 // during the first update (i == 0), the server removes old dormant_flavor_infos 468 for (int i = 0; i < newFlavorCount; i++) { 469 const flavor_info* flavorInfo; 470 TRACE("flavor %d:\n", i); 471 if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) { 472 ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for " 473 "index %d!\n", i); 474 continue; 475 } 476 477 #if DEBUG >= 2 478 DumpFlavorInfo(flavorInfo); 479 #endif 480 481 dormant_flavor_info dormantFlavorInfo; 482 dormantFlavorInfo = *flavorInfo; 483 dormantFlavorInfo.node_info.addon = addonID; 484 dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id; 485 strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name, 486 B_MEDIA_NAME_LENGTH); 487 488 size_t flattenedSize = dormantFlavorInfo.FlattenedSize(); 489 size_t messageSize = flattenedSize 490 + sizeof(server_register_dormant_node_command); 491 server_register_dormant_node_command* message 492 = (server_register_dormant_node_command*)malloc(messageSize); 493 if (message == NULL) 494 break; 495 496 // The server should remove previously registered "dormant_flavor_info"s 497 // during the first update, but after the first iteration, we don't 498 // want the server to anymore remove old dormant_flavor_infos 499 message->purge_id = i == 0 ? addonID : 0; 500 501 message->type = dormantFlavorInfo.TypeCode(); 502 message->flattened_size = flattenedSize; 503 dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize); 504 505 status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE, 506 message, messageSize); 507 if (status != B_OK) { 508 ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register " 509 "dormant node: %s\n", strerror(status)); 510 } 511 512 free(message); 513 } 514 515 // TODO: we currently pretend that all old flavors have been removed, this 516 // could probably be done in a smarter way 517 BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount, 518 oldFlavorCount); 519 } 520 521 522 void 523 MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode) 524 { 525 TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path); 526 527 media_addon_id id = gDormantNodeManager->RegisterAddOn(path); 528 if (id <= 0) { 529 ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n", 530 path); 531 return; 532 } 533 534 TRACE("MediaAddonServer::_AddOnAdded: loading addon %" B_PRId32 " now..." 535 "\n", id); 536 537 BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id); 538 if (addon == NULL) { 539 ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n", 540 path); 541 gDormantNodeManager->UnregisterAddOn(id); 542 return; 543 } 544 545 TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %" B_PRId32 546 "\n", id); 547 548 try { 549 // put file's inode and addon's id into map 550 fFileMap.insert(std::make_pair(fileNode, id)); 551 552 AddOnInfo info = {}; 553 fInfoMap.insert(std::make_pair(id, info)); 554 } catch (std::bad_alloc& exception) { 555 fFileMap.erase(fileNode); 556 return; 557 } 558 559 InfoMap::iterator found = fInfoMap.find(id); 560 AddOnInfo& info = found->second; 561 562 info.id = id; 563 // temporary default 564 info.wants_autostart = false; 565 info.flavor_count = 0; 566 info.addon = addon; 567 568 // scan the flavors 569 _ScanAddOnFlavors(addon); 570 571 // need to call BMediaNode::WantsAutoStart() 572 // after the flavors have been scanned 573 info.wants_autostart = addon->WantsAutoStart(); 574 575 if (info.wants_autostart) 576 TRACE("add-on %" B_PRId32 " WantsAutoStart!\n", id); 577 578 // During startup, first all add-ons are loaded, then all 579 // nodes (flavors) representing physical inputs and outputs 580 // are instantiated. Next, all add-ons that need autostart 581 // will be autostarted. Finally, add-ons that don't have 582 // any active nodes (flavors) will be unloaded. 583 584 // After startup is done, we simply do it for each new 585 // loaded add-on, too. 586 if (!fStartup) { 587 _InstantiatePhysicalInputsAndOutputs(info); 588 _InstantiateAutostartFlavors(info); 589 _PutAddonIfPossible(info); 590 591 // since something might have changed 592 server_rescan_defaults_command cmd; 593 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 594 } 595 596 // we do not call gDormantNodeManager->PutAddOn(id) 597 // since it is done by _PutAddonIfPossible() 598 } 599 600 601 void 602 MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info) 603 { 604 printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %" B_PRId32 605 "\n", info.id); 606 607 NodeVector::iterator iterator = info.active_flavors.begin(); 608 for (; iterator != info.active_flavors.end(); iterator++) { 609 media_node& node = *iterator; 610 611 printf("node %" B_PRId32 "\n", node.node); 612 613 if ((node.kind & B_TIME_SOURCE) != 0 614 && (fMediaRoster->StopTimeSource(node, 0, true) != B_OK)) { 615 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop " 616 "timesource\n"); 617 continue; 618 } 619 620 if (fMediaRoster->StopNode(node, 0, true) != B_OK) { 621 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop " 622 "node\n"); 623 continue; 624 } 625 626 if ((node.kind & B_BUFFER_CONSUMER) != 0) { 627 media_input inputs[16]; 628 int32 count = 0; 629 if (fMediaRoster->GetConnectedInputsFor(node, inputs, 16, &count) 630 != B_OK) { 631 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't " 632 "get connected inputs\n"); 633 continue; 634 } 635 636 for (int32 i = 0; i < count; i++) { 637 media_node_id sourceNode; 638 if ((sourceNode = fMediaRoster->NodeIDFor( 639 inputs[i].source.port)) < 0) { 640 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 641 "couldn't get source node id\n"); 642 continue; 643 } 644 645 if (fMediaRoster->Disconnect(sourceNode, inputs[i].source, 646 node.node, inputs[i].destination) != B_OK) { 647 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 648 "couldn't disconnect input\n"); 649 continue; 650 } 651 } 652 } 653 654 if ((node.kind & B_BUFFER_PRODUCER) != 0) { 655 media_output outputs[16]; 656 int32 count = 0; 657 if (fMediaRoster->GetConnectedOutputsFor(node, outputs, 16, 658 &count) != B_OK) { 659 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't " 660 "get connected outputs\n"); 661 continue; 662 } 663 664 for (int32 i = 0; i < count; i++) { 665 media_node_id destNode; 666 if ((destNode = fMediaRoster->NodeIDFor( 667 outputs[i].destination.port)) < 0) { 668 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 669 "couldn't get destination node id\n"); 670 continue; 671 } 672 673 if (fMediaRoster->Disconnect(node.node, outputs[i].source, 674 destNode, outputs[i].destination) != B_OK) { 675 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 676 "couldn't disconnect output\n"); 677 continue; 678 } 679 } 680 } 681 682 if (MediaRosterEx(fMediaRoster)->ReleaseNodeAll(node) != B_OK) { 683 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 684 "couldn't release node\n"); 685 } 686 687 // wait a bit to let the node clean up 688 snooze(50000); 689 } 690 691 info.active_flavors.clear(); 692 } 693 694 695 void 696 MediaAddonServer::_PutAddonIfPossible(AddOnInfo& info) 697 { 698 if (info.addon && info.active_flavors.empty()) { 699 gDormantNodeManager->PutAddOn(info.id); 700 info.addon = NULL; 701 } 702 } 703 704 705 void 706 MediaAddonServer::_InstantiatePhysicalInputsAndOutputs(AddOnInfo& info) 707 { 708 CALLED(); 709 int32 count = info.addon->CountFlavors(); 710 711 for (int32 i = 0; i < count; i++) { 712 const flavor_info* flavorinfo; 713 if (info.addon->GetFlavorAt(i, &flavorinfo) != B_OK) { 714 ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs " 715 "GetFlavorAt failed for index %" B_PRId32 "!\n", i); 716 continue; 717 } 718 if ((flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) != 0) { 719 media_node node; 720 721 dormant_node_info dormantNodeInfo; 722 dormantNodeInfo.addon = info.id; 723 dormantNodeInfo.flavor_id = flavorinfo->internal_id; 724 strcpy(dormantNodeInfo.name, flavorinfo->name); 725 726 TRACE("MediaAddonServer::InstantiatePhysialInputsAndOutputs: " 727 "\"%s\" is a physical input/output\n", flavorinfo->name); 728 status_t status = fMediaRoster->InstantiateDormantNode( 729 dormantNodeInfo, &node); 730 if (status != B_OK) { 731 ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs " 732 "Couldn't instantiate node flavor, internal_id %" B_PRId32 733 ", name %s\n", flavorinfo->internal_id, flavorinfo->name); 734 } else { 735 TRACE("Node created!\n"); 736 info.active_flavors.push_back(node); 737 } 738 } 739 } 740 } 741 742 743 void 744 MediaAddonServer::_InstantiateAutostartFlavors(AddOnInfo& info) 745 { 746 if (!info.wants_autostart) 747 return; 748 749 for (int32 index = 0;; index++) { 750 TRACE("trying autostart of node %" B_PRId32 ", index %" B_PRId32 "\n", 751 info.id, index); 752 753 BMediaNode* node; 754 int32 internalID; 755 bool hasMore; 756 status_t status = info.addon->AutoStart(index, &node, &internalID, 757 &hasMore); 758 if (status == B_MEDIA_ADDON_FAILED && hasMore) 759 continue; 760 else if (status != B_OK) 761 break; 762 763 printf("started node %" B_PRId32 "\n", index); 764 765 status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id, 766 internalID); 767 if (status != B_OK) { 768 ERROR("failed to register node %" B_PRId32 "\n", index); 769 node->Release(); 770 } else { 771 MediaRosterEx(fMediaRoster)->IncrementAddonFlavorInstancesCount( 772 info.id, internalID); 773 info.active_flavors.push_back(node->Node()); 774 } 775 776 if (!hasMore) 777 return; 778 } 779 } 780 781 782 void 783 MediaAddonServer::_AddOnRemoved(ino_t fileNode) 784 { 785 // TODO: locking? 786 787 FileMap::iterator foundFile = fFileMap.find(fileNode); 788 if (foundFile == fFileMap.end()) { 789 ERROR("MediaAddonServer::_AddOnRemoved: inode %" B_PRIdINO " removed, but no " 790 "media add-on found\n", fileNode); 791 return; 792 } 793 794 media_addon_id id = foundFile->second; 795 fFileMap.erase(foundFile); 796 797 int32 oldFlavorCount; 798 InfoMap::iterator foundInfo = fInfoMap.find(id); 799 800 if (foundInfo == fInfoMap.end()) { 801 ERROR("MediaAddonServer::_AddOnRemoved: couldn't get addon info for " 802 "add-on %" B_PRId32 "\n", id); 803 oldFlavorCount = 1000; 804 } else { 805 AddOnInfo& info = foundInfo->second; 806 oldFlavorCount = info.flavor_count; 807 808 _DestroyInstantiatedFlavors(info); 809 _PutAddonIfPossible(info); 810 811 if (info.addon) { 812 ERROR("MediaAddonServer::_AddOnRemoved: couldn't unload addon " 813 "%" B_PRId32 " since flavors are in use\n", id); 814 } 815 816 fInfoMap.erase(foundInfo); 817 } 818 819 gDormantNodeManager->UnregisterAddOn(id); 820 821 BPrivate::media::notifications::FlavorsChanged(id, 0, oldFlavorCount); 822 } 823 824 825 // #pragma mark - 826 827 828 int 829 main() 830 { 831 new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE); 832 be_app->Run(); 833 delete be_app; 834 return 0; 835 } 836