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_ADDONS_DIRECTORY, 298 B_COMMON_ADDONS_DIRECTORY, 299 B_SYSTEM_ADDONS_DIRECTORY 300 }; 301 302 // when safemode, only B_SYSTEM_ADDONS_DIRECTORY is used 303 for (uint32 i = safeMode ? 4 : 0; 304 i < sizeof(directories) / sizeof(directory_which); i++) { 305 BDirectory directory; 306 node_ref nodeRef; 307 BPath path; 308 if (find_directory(directories[i], &path) == B_OK 309 && path.Append("media") == B_OK 310 && directory.SetTo(path.Path()) == B_OK 311 && directory.GetNodeRef(&nodeRef) == B_OK) 312 fMonitorHandler->AddDirectory(&nodeRef); 313 } 314 315 #ifdef USER_ADDON_PATH 316 node_ref nodeRef; 317 if (entry.SetTo(USER_ADDON_PATH) == B_OK 318 && entry.GetNodeRef(&nodeRef) == B_OK) 319 fMonitorHandler->AddDirectory(&nodeRef); 320 #endif 321 322 fStartup = false; 323 324 InfoMap::iterator iterator = fInfoMap.begin(); 325 for (; iterator != fInfoMap.end(); iterator++) 326 _InstantiatePhysicalInputsAndOutputs(iterator->second); 327 328 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 329 _InstantiateAutostartFlavors(iterator->second); 330 331 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 332 _PutAddonIfPossible(iterator->second); 333 334 server_rescan_defaults_command cmd; 335 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 336 } 337 338 339 bool 340 MediaAddonServer::QuitRequested() 341 { 342 CALLED(); 343 344 InfoMap::iterator iterator = fInfoMap.begin(); 345 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 346 _DestroyInstantiatedFlavors(iterator->second); 347 348 BMediaRoster::CurrentRoster()->Lock(); 349 BMediaRoster::CurrentRoster()->Quit(); 350 351 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 352 _PutAddonIfPossible(iterator->second); 353 354 return true; 355 } 356 357 358 void 359 MediaAddonServer::MessageReceived(BMessage* message) 360 { 361 switch (message->what) { 362 case MEDIA_ADD_ON_SERVER_PLAY_MEDIA: 363 { 364 const char* name; 365 const char* type; 366 if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK 367 || message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) { 368 message->SendReply(B_ERROR); 369 break; 370 } 371 372 PlayMediaFile(type, name); 373 message->SendReply((uint32)B_OK); 374 // TODO: don't know which reply is expected 375 return; 376 } 377 378 default: 379 BApplication::MessageReceived(message); 380 break; 381 } 382 } 383 384 385 void 386 MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size) 387 { 388 switch (code) { 389 case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE: 390 { 391 const add_on_server_instantiate_dormant_node_request* request 392 = static_cast< 393 const add_on_server_instantiate_dormant_node_request*>( 394 data); 395 add_on_server_instantiate_dormant_node_reply reply; 396 397 status_t status 398 = MediaRosterEx(fMediaRoster)->InstantiateDormantNode( 399 request->add_on_id, request->flavor_id, 400 request->creator_team, &reply.node); 401 request->SendReply(status, &reply, sizeof(reply)); 402 break; 403 } 404 405 case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS: 406 { 407 const add_on_server_rescan_flavors_command* command = static_cast< 408 const add_on_server_rescan_flavors_command*>(data); 409 BMediaAddOn* addOn 410 = gDormantNodeManager->GetAddOn(command->add_on_id); 411 if (addOn == NULL) { 412 ERROR("rescan flavors: Can't find a addon object for id %d\n", 413 (int)command->add_on_id); 414 break; 415 } 416 _ScanAddOnFlavors(addOn); 417 gDormantNodeManager->PutAddOn(command->add_on_id); 418 break; 419 } 420 421 case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY: 422 if (fStartupSound) { 423 system_beep(MEDIA_SOUNDS_STARTUP); 424 fStartupSound = false; 425 } 426 break; 427 428 default: 429 ERROR("media_addon_server: received unknown message code %#08lx\n", 430 code); 431 break; 432 } 433 } 434 435 436 status_t 437 MediaAddonServer::_ControlThread(void* _server) 438 { 439 MediaAddonServer* server = (MediaAddonServer*)_server; 440 441 char data[B_MEDIA_MESSAGE_SIZE]; 442 ssize_t size; 443 int32 code; 444 while ((size = read_port_etc(server->_ControlPort(), &code, data, 445 sizeof(data), 0, 0)) > 0) 446 server->_HandleMessage(code, data, size); 447 448 return B_OK; 449 } 450 451 452 void 453 MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon) 454 { 455 ASSERT(addon->AddonID() > 0); 456 457 TRACE("MediaAddonServer::_ScanAddOnFlavors: id %ld\n", addon->AddonID()); 458 459 // cache the media_addon_id in a local variable to avoid 460 // calling BMediaAddOn::AddonID() too often 461 media_addon_id addonID = addon->AddonID(); 462 463 // update the cached flavor count, get oldflavorcount and newflavorcount 464 465 InfoMap::iterator found = fInfoMap.find(addonID); 466 ASSERT(found != fInfoMap.end()); 467 468 AddOnInfo& info = found->second; 469 int32 oldFlavorCount = info.flavor_count; 470 int32 newFlavorCount = addon->CountFlavors(); 471 info.flavor_count = newFlavorCount; 472 473 TRACE("%ld old flavors, %ld new flavors\n", oldflavorcount, newFlavorCount); 474 475 // during the first update (i == 0), the server removes old dormant_flavor_infos 476 for (int i = 0; i < newFlavorCount; i++) { 477 const flavor_info* flavorInfo; 478 TRACE("flavor %d:\n", i); 479 if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) { 480 ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for " 481 "index %d!\n", i); 482 continue; 483 } 484 485 #if DEBUG >= 2 486 DumpFlavorInfo(flavorInfo); 487 #endif 488 489 dormant_flavor_info dormantFlavorInfo; 490 dormantFlavorInfo = *flavorInfo; 491 dormantFlavorInfo.node_info.addon = addonID; 492 dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id; 493 strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name, 494 B_MEDIA_NAME_LENGTH); 495 496 size_t flattenedSize = dormantFlavorInfo.FlattenedSize(); 497 size_t messageSize = flattenedSize 498 + sizeof(server_register_dormant_node_command); 499 server_register_dormant_node_command* message 500 = (server_register_dormant_node_command*)malloc(messageSize); 501 if (message == NULL) 502 break; 503 504 // The server should remove previously registered "dormant_flavor_info"s 505 // during the first update, but after the first iteration, we don't 506 // want the server to anymore remove old dormant_flavor_infos 507 message->purge_id = i == 0 ? addonID : 0; 508 509 message->type = dormantFlavorInfo.TypeCode(); 510 message->flattened_size = flattenedSize; 511 dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize); 512 513 status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE, 514 message, messageSize); 515 if (status != B_OK) { 516 ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register " 517 "dormant node: %s\n", strerror(status)); 518 } 519 520 free(message); 521 } 522 523 // TODO: we currently pretend that all old flavors have been removed, this 524 // could probably be done in a smarter way 525 BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount, 526 oldFlavorCount); 527 } 528 529 530 void 531 MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode) 532 { 533 TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path); 534 535 media_addon_id id = gDormantNodeManager->RegisterAddOn(path); 536 if (id <= 0) { 537 ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n", 538 path); 539 return; 540 } 541 542 TRACE("MediaAddonServer::_AddOnAdded: loading addon %ld now...\n", id); 543 544 BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id); 545 if (addon == NULL) { 546 ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n", path); 547 gDormantNodeManager->UnregisterAddOn(id); 548 return; 549 } 550 551 TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %ld\n", id); 552 553 try { 554 // put file's inode and addon's id into map 555 fFileMap.insert(std::make_pair(fileNode, id)); 556 557 AddOnInfo info; 558 fInfoMap.insert(std::make_pair(id, info)); 559 } catch (std::bad_alloc& exception) { 560 fFileMap.erase(fileNode); 561 return; 562 } 563 564 InfoMap::iterator found = fInfoMap.find(id); 565 AddOnInfo& info = found->second; 566 567 info.id = id; 568 info.wants_autostart = false; // temporary default 569 info.flavor_count = 0; 570 info.addon = addon; 571 572 // scan the flavors 573 _ScanAddOnFlavors(addon); 574 575 // need to call BMediaNode::WantsAutoStart() 576 // after the flavors have been scanned 577 info.wants_autostart = addon->WantsAutoStart(); 578 579 if (info.wants_autostart) 580 TRACE("add-on %ld WantsAutoStart!\n", id); 581 582 // During startup, first all add-ons are loaded, then all 583 // nodes (flavors) representing physical inputs and outputs 584 // are instantiated. Next, all add-ons that need autostart 585 // will be autostarted. Finally, add-ons that don't have 586 // any active nodes (flavors) will be unloaded. 587 588 // After startup is done, we simply do it for each new 589 // loaded add-on, too. 590 if (!fStartup) { 591 _InstantiatePhysicalInputsAndOutputs(info); 592 _InstantiateAutostartFlavors(info); 593 _PutAddonIfPossible(info); 594 595 // since something might have changed 596 server_rescan_defaults_command cmd; 597 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 598 } 599 600 // we do not call gDormantNodeManager->PutAddOn(id) 601 // since it is done by _PutAddonIfPossible() 602 } 603 604 605 void 606 MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info) 607 { 608 printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %ld\n", info.id); 609 610 NodeVector::iterator iterator = info.active_flavors.begin(); 611 for (; iterator != info.active_flavors.end(); iterator++) { 612 media_node& node = *iterator; 613 614 printf("node %ld\n", node.node); 615 616 if ((node.kind & B_TIME_SOURCE) != 0 617 && (fMediaRoster->StopTimeSource(node, 0, true) != B_OK)) { 618 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop " 619 "timesource\n"); 620 continue; 621 } 622 623 if (fMediaRoster->StopNode(node, 0, true) != B_OK) { 624 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't stop " 625 "node\n"); 626 continue; 627 } 628 629 if ((node.kind & B_BUFFER_CONSUMER) != 0) { 630 media_input inputs[16]; 631 int32 count = 0; 632 if (fMediaRoster->GetConnectedInputsFor(node, inputs, 16, &count) 633 != B_OK) { 634 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't " 635 "get connected inputs\n"); 636 continue; 637 } 638 639 for (int32 i = 0; i < count; i++) { 640 media_node_id sourceNode; 641 if ((sourceNode = fMediaRoster->NodeIDFor( 642 inputs[i].source.port)) < 0) { 643 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 644 "couldn't get source node id\n"); 645 continue; 646 } 647 648 if (fMediaRoster->Disconnect(sourceNode, inputs[i].source, 649 node.node, inputs[i].destination) != B_OK) { 650 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 651 "couldn't disconnect input\n"); 652 continue; 653 } 654 } 655 } 656 657 if ((node.kind & B_BUFFER_PRODUCER) != 0) { 658 media_output outputs[16]; 659 int32 count = 0; 660 if (fMediaRoster->GetConnectedOutputsFor(node, outputs, 16, 661 &count) != B_OK) { 662 printf("MediaAddonServer::_DestroyInstantiatedFlavors couldn't " 663 "get connected outputs\n"); 664 continue; 665 } 666 667 for (int32 i = 0; i < count; i++) { 668 media_node_id destNode; 669 if ((destNode = fMediaRoster->NodeIDFor( 670 outputs[i].destination.port)) < 0) { 671 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 672 "couldn't get destination node id\n"); 673 continue; 674 } 675 676 if (fMediaRoster->Disconnect(node.node, outputs[i].source, 677 destNode, outputs[i].destination) != B_OK) { 678 printf("MediaAddonServer::_DestroyInstantiatedFlavors " 679 "couldn't disconnect output\n"); 680 continue; 681 } 682 } 683 } 684 685 MediaRosterEx(fMediaRoster)->ReleaseNodeAll(node); 686 } 687 688 info.active_flavors.clear(); 689 } 690 691 692 void 693 MediaAddonServer::_PutAddonIfPossible(AddOnInfo& info) 694 { 695 if (info.addon && info.active_flavors.empty()) { 696 gDormantNodeManager->PutAddOn(info.id); 697 info.addon = NULL; 698 } 699 } 700 701 702 void 703 MediaAddonServer::_InstantiatePhysicalInputsAndOutputs(AddOnInfo& info) 704 { 705 CALLED(); 706 int32 count = info.addon->CountFlavors(); 707 708 for (int32 i = 0; i < count; i++) { 709 const flavor_info* flavorinfo; 710 if (info.addon->GetFlavorAt(i, &flavorinfo) != B_OK) { 711 ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs " 712 "GetFlavorAt failed for index %ld!\n", i); 713 continue; 714 } 715 if ((flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) != 0) { 716 media_node node; 717 718 dormant_node_info dormantNodeInfo; 719 dormantNodeInfo.addon = info.id; 720 dormantNodeInfo.flavor_id = flavorinfo->internal_id; 721 strcpy(dormantNodeInfo.name, flavorinfo->name); 722 723 PRINT("MediaAddonServer::InstantiatePhysialInputsAndOutputs: " 724 "\"%s\" is a physical input/output\n", flavorinfo->name); 725 status_t status = fMediaRoster->InstantiateDormantNode( 726 dormantNodeInfo, &node); 727 if (status != B_OK) { 728 ERROR("MediaAddonServer::InstantiatePhysialInputsAndOutputs " 729 "Couldn't instantiate node flavor, internal_id %ld, " 730 "name %s\n", flavorinfo->internal_id, flavorinfo->name); 731 } else { 732 PRINT("Node created!\n"); 733 info.active_flavors.push_back(node); 734 } 735 } 736 } 737 } 738 739 740 void 741 MediaAddonServer::_InstantiateAutostartFlavors(AddOnInfo& info) 742 { 743 if (!info.wants_autostart) 744 return; 745 746 for (int32 index = 0;; index++) { 747 PRINT("trying autostart of node %ld, index %ld\n", info.id, index); 748 749 BMediaNode* node; 750 int32 internalID; 751 bool hasMore; 752 status_t status = info.addon->AutoStart(index, &node, &internalID, 753 &hasMore); 754 if (status == B_MEDIA_ADDON_FAILED && hasMore) 755 continue; 756 else if (status != B_OK) 757 break; 758 759 printf("started node %ld\n", index); 760 761 status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id, 762 internalID); 763 if (status != B_OK) { 764 ERROR("failed to register node %ld\n", index); 765 node->Release(); 766 } else { 767 MediaRosterEx(fMediaRoster)->IncrementAddonFlavorInstancesCount( 768 info.id, internalID); 769 info.active_flavors.push_back(node->Node()); 770 } 771 772 if (!hasMore) 773 return; 774 } 775 } 776 777 778 void 779 MediaAddonServer::_AddOnRemoved(ino_t fileNode) 780 { 781 // TODO: locking? 782 783 FileMap::iterator foundFile = fFileMap.find(fileNode); 784 if (foundFile == fFileMap.end()) { 785 ERROR("MediaAddonServer::_AddOnRemoved: inode %Ld removed, but no " 786 "media add-on found\n", fileNode); 787 return; 788 } 789 790 media_addon_id id = foundFile->second; 791 fFileMap.erase(foundFile); 792 793 int32 oldFlavorCount; 794 InfoMap::iterator foundInfo = fInfoMap.find(id); 795 796 if (foundInfo == fInfoMap.end()) { 797 ERROR("MediaAddonServer::_AddOnRemoved: couldn't get addon info for " 798 "add-on %ld\n", id); 799 oldFlavorCount = 1000; 800 } else { 801 AddOnInfo& info = foundInfo->second; 802 oldFlavorCount = info.flavor_count; 803 804 _DestroyInstantiatedFlavors(info); 805 _PutAddonIfPossible(info); 806 807 if (info.addon) { 808 ERROR("MediaAddonServer::_AddOnRemoved: couldn't unload addon " 809 "%ld since flavors are in use\n", id); 810 } 811 812 fInfoMap.erase(foundInfo); 813 } 814 815 gDormantNodeManager->UnregisterAddOn(id); 816 817 BPrivate::media::notifications::FlavorsChanged(id, 0, oldFlavorCount); 818 } 819 820 821 // #pragma mark - 822 823 824 int 825 main() 826 { 827 new MediaAddonServer(B_MEDIA_ADDON_SERVER_SIGNATURE); 828 be_app->Run(); 829 delete be_app; 830 return 0; 831 } 832 833