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 <debug.h> 51 #include <DataExchange.h> 52 #include <DormantNodeManager.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 BMediaAddOn* addon; 77 // if != NULL, need to call gDormantNodeManager->PutAddOn(id) 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 }; 125 126 127 class MediaAddonServer::MonitorHandler : public AddOnMonitorHandler { 128 public: 129 MonitorHandler(MediaAddonServer* server); 130 131 virtual void AddOnEnabled(const add_on_entry_info* info); 132 virtual void AddOnDisabled(const add_on_entry_info* info); 133 134 private: 135 MediaAddonServer* fServer; 136 }; 137 138 139 #if DEBUG >= 2 140 static void 141 DumpFlavorInfo(const flavor_info* info) 142 { 143 printf(" name = %s\n", info->name); 144 printf(" info = %s\n", info->info); 145 printf(" internal_id = %ld\n", info->internal_id); 146 printf(" possible_count = %ld\n", info->possible_count); 147 printf(" flavor_flags = 0x%lx", info->flavor_flags); 148 if (info->flavor_flags & B_FLAVOR_IS_GLOBAL) printf(" B_FLAVOR_IS_GLOBAL"); 149 if (info->flavor_flags & B_FLAVOR_IS_LOCAL) printf(" B_FLAVOR_IS_LOCAL"); 150 printf("\n"); 151 printf(" kinds = 0x%Lx", info->kinds); 152 if (info->kinds & B_BUFFER_PRODUCER) printf(" B_BUFFER_PRODUCER"); 153 if (info->kinds & B_BUFFER_CONSUMER) printf(" B_BUFFER_CONSUMER"); 154 if (info->kinds & B_TIME_SOURCE) printf(" B_TIME_SOURCE"); 155 if (info->kinds & B_CONTROLLABLE) printf(" B_CONTROLLABLE"); 156 if (info->kinds & B_FILE_INTERFACE) printf(" B_FILE_INTERFACE"); 157 if (info->kinds & B_ENTITY_INTERFACE) printf(" B_ENTITY_INTERFACE"); 158 if (info->kinds & B_PHYSICAL_INPUT) printf(" B_PHYSICAL_INPUT"); 159 if (info->kinds & B_PHYSICAL_OUTPUT) printf(" B_PHYSICAL_OUTPUT"); 160 if (info->kinds & B_SYSTEM_MIXER) printf(" B_SYSTEM_MIXER"); 161 printf("\n"); 162 printf(" in_format_count = %ld\n", info->in_format_count); 163 printf(" out_format_count = %ld\n", info->out_format_count); 164 } 165 #endif 166 167 168 // #pragma mark - 169 170 171 MediaAddonServer::MonitorHandler::MonitorHandler(MediaAddonServer* server) 172 { 173 fServer = server; 174 } 175 176 177 void 178 MediaAddonServer::MonitorHandler::AddOnEnabled(const add_on_entry_info* info) 179 { 180 entry_ref ref; 181 make_entry_ref(info->dir_nref.device, info->dir_nref.node, 182 info->name, &ref); 183 BEntry entry(&ref, true); 184 if (!entry.IsFile()) 185 return; 186 187 BPath path(&ref); 188 if (path.InitCheck() == B_OK) 189 fServer->_AddOnAdded(path.Path(), info->nref.node); 190 } 191 192 193 void 194 MediaAddonServer::MonitorHandler::AddOnDisabled(const add_on_entry_info* info) 195 { 196 fServer->_AddOnRemoved(info->nref.node); 197 } 198 199 200 // #pragma mark - 201 202 203 MediaAddonServer::MediaAddonServer(const char* signature) 204 : 205 BApplication(signature), 206 fStartup(true), 207 fStartupSound(true) 208 { 209 CALLED(); 210 fMediaRoster = BMediaRoster::Roster(); 211 fControlPort = create_port(64, MEDIA_ADDON_SERVER_PORT_NAME); 212 fControlThread = spawn_thread(_ControlThread, "media_addon_server control", 213 B_NORMAL_PRIORITY + 2, this); 214 resume_thread(fControlThread); 215 } 216 217 218 MediaAddonServer::~MediaAddonServer() 219 { 220 CALLED(); 221 222 delete_port(fControlPort); 223 wait_for_thread(fControlThread, NULL); 224 225 // unregister all media add-ons 226 FileMap::iterator iterator = fFileMap.begin(); 227 for (; iterator != fFileMap.end(); iterator++) 228 gDormantNodeManager->UnregisterAddOn(iterator->second); 229 230 // TODO: unregister system time source 231 } 232 233 234 void 235 MediaAddonServer::ReadyToRun() 236 { 237 if (!be_roster->IsRunning("application/x-vnd.Be.media-server")) { 238 // the media server is not running, let's quit 239 fprintf(stderr, "The media_server is not running!\n"); 240 Quit(); 241 return; 242 } 243 244 // the control thread is already running at this point, 245 // so we can talk to the media server and also receive 246 // commands for instantiation 247 248 ASSERT(fStartup == true); 249 250 // The very first thing to do is to create the system time source, 251 // register it with the server, and make it the default SYSTEM_TIME_SOURCE 252 BMediaNode *timeSource = new SystemTimeSource; 253 status_t result = fMediaRoster->RegisterNode(timeSource); 254 if (result != B_OK) { 255 fprintf(stderr, "Can't register system time source : %s\n", 256 strerror(result)); 257 debugger("Can't register system time source"); 258 } 259 260 if (timeSource->ID() != NODE_SYSTEM_TIMESOURCE_ID) 261 debugger("System time source got wrong node ID"); 262 media_node node = timeSource->Node(); 263 result = MediaRosterEx(fMediaRoster)->SetNode(SYSTEM_TIME_SOURCE, &node); 264 if (result != B_OK) 265 debugger("Can't setup system time source as default"); 266 267 // During startup, first all add-ons are loaded, then all 268 // nodes (flavors) representing physical inputs and outputs 269 // are instantiated. Next, all add-ons that need autostart 270 // will be autostarted. Finally, add-ons that don't have 271 // any active nodes (flavors) will be unloaded. 272 273 fMonitorHandler = new MonitorHandler(this); 274 AddHandler(fMonitorHandler); 275 276 BMessage pulse(B_PULSE); 277 fPulseRunner = new BMessageRunner(fMonitorHandler, &pulse, 1000000LL); 278 // the monitor handler needs a pulse to check if add-ons are ready 279 280 fMonitorHandler->AddAddOnDirectories("media"); 281 282 #ifdef USER_ADDON_PATH 283 node_ref nodeRef; 284 if (entry.SetTo(USER_ADDON_PATH) == B_OK 285 && entry.GetNodeRef(&nodeRef) == B_OK) { 286 fMonitorHandler->AddDirectory(&nodeRef); 287 } 288 #endif 289 290 fStartup = false; 291 292 InfoMap::iterator iterator = fInfoMap.begin(); 293 for (; iterator != fInfoMap.end(); iterator++) 294 _InstantiatePhysicalInputsAndOutputs(iterator->second); 295 296 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 297 _InstantiateAutostartFlavors(iterator->second); 298 299 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 300 _PutAddonIfPossible(iterator->second); 301 302 server_rescan_defaults_command cmd; 303 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 304 } 305 306 307 bool 308 MediaAddonServer::QuitRequested() 309 { 310 CALLED(); 311 312 InfoMap::iterator iterator = fInfoMap.begin(); 313 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 314 _DestroyInstantiatedFlavors(iterator->second); 315 316 BMediaRoster::CurrentRoster()->Lock(); 317 BMediaRoster::CurrentRoster()->Quit(); 318 319 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 320 _PutAddonIfPossible(iterator->second); 321 322 return true; 323 } 324 325 326 void 327 MediaAddonServer::MessageReceived(BMessage* message) 328 { 329 switch (message->what) { 330 case MEDIA_ADD_ON_SERVER_PLAY_MEDIA: 331 { 332 const char* name; 333 const char* type; 334 if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK 335 || message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) { 336 message->SendReply(B_ERROR); 337 break; 338 } 339 340 PlayMediaFile(type, name); 341 message->SendReply((uint32)B_OK); 342 // TODO: don't know which reply is expected 343 return; 344 } 345 346 default: 347 BApplication::MessageReceived(message); 348 break; 349 } 350 } 351 352 353 void 354 MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size) 355 { 356 switch (code) { 357 case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE: 358 { 359 const add_on_server_instantiate_dormant_node_request* request 360 = static_cast< 361 const add_on_server_instantiate_dormant_node_request*>( 362 data); 363 add_on_server_instantiate_dormant_node_reply reply; 364 365 status_t status 366 = MediaRosterEx(fMediaRoster)->InstantiateDormantNode( 367 request->add_on_id, request->flavor_id, 368 request->creator_team, &reply.node); 369 request->SendReply(status, &reply, sizeof(reply)); 370 break; 371 } 372 373 case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS: 374 { 375 const add_on_server_rescan_flavors_command* command = static_cast< 376 const add_on_server_rescan_flavors_command*>(data); 377 BMediaAddOn* addOn 378 = gDormantNodeManager->GetAddOn(command->add_on_id); 379 if (addOn == NULL) { 380 ERROR("rescan flavors: Can't find a addon object for id %d\n", 381 (int)command->add_on_id); 382 break; 383 } 384 _ScanAddOnFlavors(addOn); 385 gDormantNodeManager->PutAddOn(command->add_on_id); 386 break; 387 } 388 389 case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY: 390 if (fStartupSound) { 391 system_beep(MEDIA_SOUNDS_STARTUP); 392 fStartupSound = false; 393 } 394 break; 395 396 default: 397 ERROR("media_addon_server: received unknown message code %#08" 398 B_PRIx32 "\n", code); 399 break; 400 } 401 } 402 403 404 status_t 405 MediaAddonServer::_ControlThread(void* _server) 406 { 407 MediaAddonServer* server = (MediaAddonServer*)_server; 408 409 char data[B_MEDIA_MESSAGE_SIZE]; 410 ssize_t size; 411 int32 code; 412 while ((size = read_port_etc(server->_ControlPort(), &code, data, 413 sizeof(data), 0, 0)) > 0) 414 server->_HandleMessage(code, data, size); 415 416 return B_OK; 417 } 418 419 420 void 421 MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon) 422 { 423 ASSERT(addon->AddonID() > 0); 424 425 TRACE("MediaAddonServer::_ScanAddOnFlavors: id %ld\n", addon->AddonID()); 426 427 // cache the media_addon_id in a local variable to avoid 428 // calling BMediaAddOn::AddonID() too often 429 media_addon_id addonID = addon->AddonID(); 430 431 // update the cached flavor count, get oldflavorcount and newflavorcount 432 433 InfoMap::iterator found = fInfoMap.find(addonID); 434 ASSERT(found != fInfoMap.end()); 435 436 AddOnInfo& info = found->second; 437 int32 oldFlavorCount = info.flavor_count; 438 int32 newFlavorCount = addon->CountFlavors(); 439 info.flavor_count = newFlavorCount; 440 441 TRACE("%ld old flavors, %ld new flavors\n", oldFlavorCount, newFlavorCount); 442 443 // during the first update (i == 0), the server removes old dormant_flavor_infos 444 for (int i = 0; i < newFlavorCount; i++) { 445 const flavor_info* flavorInfo; 446 TRACE("flavor %d:\n", i); 447 if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) { 448 ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for " 449 "index %d!\n", i); 450 continue; 451 } 452 453 #if DEBUG >= 2 454 DumpFlavorInfo(flavorInfo); 455 #endif 456 457 dormant_flavor_info dormantFlavorInfo; 458 dormantFlavorInfo = *flavorInfo; 459 dormantFlavorInfo.node_info.addon = addonID; 460 dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id; 461 strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name, 462 B_MEDIA_NAME_LENGTH); 463 464 size_t flattenedSize = dormantFlavorInfo.FlattenedSize(); 465 size_t messageSize = flattenedSize 466 + sizeof(server_register_dormant_node_command); 467 server_register_dormant_node_command* message 468 = (server_register_dormant_node_command*)malloc(messageSize); 469 if (message == NULL) 470 break; 471 472 // The server should remove previously registered "dormant_flavor_info"s 473 // during the first update, but after the first iteration, we don't 474 // want the server to anymore remove old dormant_flavor_infos 475 message->purge_id = i == 0 ? addonID : 0; 476 477 message->type = dormantFlavorInfo.TypeCode(); 478 message->flattened_size = flattenedSize; 479 dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize); 480 481 status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE, 482 message, messageSize); 483 if (status != B_OK) { 484 ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register " 485 "dormant node: %s\n", strerror(status)); 486 } 487 488 free(message); 489 } 490 491 // TODO: we currently pretend that all old flavors have been removed, this 492 // could probably be done in a smarter way 493 BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount, 494 oldFlavorCount); 495 } 496 497 498 void 499 MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode) 500 { 501 TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path); 502 503 media_addon_id id = gDormantNodeManager->RegisterAddOn(path); 504 if (id <= 0) { 505 ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n", 506 path); 507 return; 508 } 509 510 TRACE("MediaAddonServer::_AddOnAdded: loading addon %ld now...\n", id); 511 512 BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id); 513 if (addon == NULL) { 514 ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n", path); 515 gDormantNodeManager->UnregisterAddOn(id); 516 return; 517 } 518 519 TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %ld\n", id); 520 521 try { 522 // put file's inode and addon's id into map 523 fFileMap.insert(std::make_pair(fileNode, id)); 524 525 AddOnInfo info; 526 fInfoMap.insert(std::make_pair(id, info)); 527 } catch (std::bad_alloc& exception) { 528 fFileMap.erase(fileNode); 529 return; 530 } 531 532 InfoMap::iterator found = fInfoMap.find(id); 533 AddOnInfo& info = found->second; 534 535 info.id = id; 536 info.wants_autostart = false; // temporary default 537 info.flavor_count = 0; 538 info.addon = addon; 539 540 // scan the flavors 541 _ScanAddOnFlavors(addon); 542 543 // need to call BMediaNode::WantsAutoStart() 544 // after the flavors have been scanned 545 info.wants_autostart = addon->WantsAutoStart(); 546 547 if (info.wants_autostart) 548 TRACE("add-on %ld WantsAutoStart!\n", id); 549 550 // During startup, first all add-ons are loaded, then all 551 // nodes (flavors) representing physical inputs and outputs 552 // are instantiated. Next, all add-ons that need autostart 553 // will be autostarted. Finally, add-ons that don't have 554 // any active nodes (flavors) will be unloaded. 555 556 // After startup is done, we simply do it for each new 557 // loaded add-on, too. 558 if (!fStartup) { 559 _InstantiatePhysicalInputsAndOutputs(info); 560 _InstantiateAutostartFlavors(info); 561 _PutAddonIfPossible(info); 562 563 // since something might have changed 564 server_rescan_defaults_command cmd; 565 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 566 } 567 568 // we do not call gDormantNodeManager->PutAddOn(id) 569 // since it is done by _PutAddonIfPossible() 570 } 571 572 573 void 574 MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info) 575 { 576 printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %" B_PRId32 577 "\n", info.id); 578 579 NodeVector::iterator iterator = info.active_flavors.begin(); 580 for (; iterator != info.active_flavors.end(); iterator++) { 581 media_node& node = *iterator; 582 583 printf("node %" B_PRId32 "\n", node.node); 584 585 if ((node.kind & B_TIME_SOURCE) != 0 586 && (fMediaRoster->StopTimeSource(node, 0, true) != B_OK)) { 587 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop " 588 "timesource\n"); 589 continue; 590 } 591 592 if (fMediaRoster->StopNode(node, 0, true) != B_OK) { 593 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop " 594 "node\n"); 595 continue; 596 } 597 598 if ((node.kind & B_BUFFER_CONSUMER) != 0) { 599 media_input inputs[16]; 600 int32 count = 0; 601 if (fMediaRoster->GetConnectedInputsFor(node, inputs, 16, &count) 602 != B_OK) { 603 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't " 604 "get connected inputs\n"); 605 continue; 606 } 607 608 for (int32 i = 0; i < count; i++) { 609 media_node_id sourceNode; 610 if ((sourceNode = fMediaRoster->NodeIDFor( 611 inputs[i].source.port)) < 0) { 612 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 613 "couldn't get source node id\n"); 614 continue; 615 } 616 617 if (fMediaRoster->Disconnect(sourceNode, inputs[i].source, 618 node.node, inputs[i].destination) != B_OK) { 619 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 620 "couldn't disconnect input\n"); 621 continue; 622 } 623 } 624 } 625 626 if ((node.kind & B_BUFFER_PRODUCER) != 0) { 627 media_output outputs[16]; 628 int32 count = 0; 629 if (fMediaRoster->GetConnectedOutputsFor(node, outputs, 16, 630 &count) != B_OK) { 631 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't " 632 "get connected outputs\n"); 633 continue; 634 } 635 636 for (int32 i = 0; i < count; i++) { 637 media_node_id destNode; 638 if ((destNode = fMediaRoster->NodeIDFor( 639 outputs[i].destination.port)) < 0) { 640 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 641 "couldn't get destination node id\n"); 642 continue; 643 } 644 645 if (fMediaRoster->Disconnect(node.node, outputs[i].source, 646 destNode, outputs[i].destination) != B_OK) { 647 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 648 "couldn't disconnect output\n"); 649 continue; 650 } 651 } 652 } 653 654 if (MediaRosterEx(fMediaRoster)->ReleaseNodeAll(node) != B_OK) { 655 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 656 "couldn't release node\n"); 657 } 658 659 // wait a bit to let the node clean up 660 snooze(50000); 661 } 662 663 info.active_flavors.clear(); 664 } 665 666 667 void 668 MediaAddonServer::_PutAddonIfPossible(AddOnInfo& info) 669 { 670 if (info.addon && info.active_flavors.empty()) { 671 gDormantNodeManager->PutAddOn(info.id); 672 info.addon = NULL; 673 } 674 } 675 676 677 void 678 MediaAddonServer::_InstantiatePhysicalInputsAndOutputs(AddOnInfo& info) 679 { 680 CALLED(); 681 int32 count = info.addon->CountFlavors(); 682 683 for (int32 i = 0; i < count; i++) { 684 const flavor_info* flavorinfo; 685 if (info.addon->GetFlavorAt(i, &flavorinfo) != B_OK) { 686 ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs " 687 "GetFlavorAt failed for index %" B_PRId32 "!\n", i); 688 continue; 689 } 690 if ((flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) != 0) { 691 media_node node; 692 693 dormant_node_info dormantNodeInfo; 694 dormantNodeInfo.addon = info.id; 695 dormantNodeInfo.flavor_id = flavorinfo->internal_id; 696 strcpy(dormantNodeInfo.name, flavorinfo->name); 697 698 TRACE("MediaAddonServer::InstantiatePhysialInputsAndOutputs: " 699 "\"%s\" is a physical input/output\n", flavorinfo->name); 700 status_t status = fMediaRoster->InstantiateDormantNode( 701 dormantNodeInfo, &node); 702 if (status != B_OK) { 703 ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs " 704 "Couldn't instantiate node flavor, internal_id %" B_PRId32 705 ", name %s\n", flavorinfo->internal_id, flavorinfo->name); 706 } else { 707 TRACE("Node created!\n"); 708 info.active_flavors.push_back(node); 709 } 710 } 711 } 712 } 713 714 715 void 716 MediaAddonServer::_InstantiateAutostartFlavors(AddOnInfo& info) 717 { 718 if (!info.wants_autostart) 719 return; 720 721 for (int32 index = 0;; index++) { 722 TRACE("trying autostart of node %ld, index %ld\n", info.id, index); 723 724 BMediaNode* node; 725 int32 internalID; 726 bool hasMore; 727 status_t status = info.addon->AutoStart(index, &node, &internalID, 728 &hasMore); 729 if (status == B_MEDIA_ADDON_FAILED && hasMore) 730 continue; 731 else if (status != B_OK) 732 break; 733 734 printf("started node %" B_PRId32 "\n", index); 735 736 status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id, 737 internalID); 738 if (status != B_OK) { 739 ERROR("failed to register node %" B_PRId32 "\n", index); 740 node->Release(); 741 } else { 742 MediaRosterEx(fMediaRoster)->IncrementAddonFlavorInstancesCount( 743 info.id, internalID); 744 info.active_flavors.push_back(node->Node()); 745 } 746 747 if (!hasMore) 748 return; 749 } 750 } 751 752 753 void 754 MediaAddonServer::_AddOnRemoved(ino_t fileNode) 755 { 756 // TODO: locking? 757 758 FileMap::iterator foundFile = fFileMap.find(fileNode); 759 if (foundFile == fFileMap.end()) { 760 ERROR("MediaAddonServer::_AddOnRemoved: inode %" B_PRIdINO " removed, but no " 761 "media add-on found\n", fileNode); 762 return; 763 } 764 765 media_addon_id id = foundFile->second; 766 fFileMap.erase(foundFile); 767 768 int32 oldFlavorCount; 769 InfoMap::iterator foundInfo = fInfoMap.find(id); 770 771 if (foundInfo == fInfoMap.end()) { 772 ERROR("MediaAddonServer::_AddOnRemoved: couldn't get addon info for " 773 "add-on %" B_PRId32 "\n", id); 774 oldFlavorCount = 1000; 775 } else { 776 AddOnInfo& info = foundInfo->second; 777 oldFlavorCount = info.flavor_count; 778 779 _DestroyInstantiatedFlavors(info); 780 _PutAddonIfPossible(info); 781 782 if (info.addon) { 783 ERROR("MediaAddonServer::_AddOnRemoved: couldn't unload addon " 784 "%" B_PRId32 " since flavors are in use\n", id); 785 } 786 787 fInfoMap.erase(foundInfo); 788 } 789 790 gDormantNodeManager->UnregisterAddOn(id); 791 792 BPrivate::media::notifications::FlavorsChanged(id, 0, oldFlavorCount); 793 } 794 795 796 // #pragma mark - 797 798 799 int 800 main() 801 { 802 new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE); 803 be_app->Run(); 804 delete be_app; 805 return 0; 806 } 807