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