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_COMMON_NONPACKAGED_ADDONS_DIRECTORY, 300 B_COMMON_ADDONS_DIRECTORY, 301 B_SYSTEM_ADDONS_DIRECTORY 302 }; 303 304 // when safemode, only B_SYSTEM_ADDONS_DIRECTORY is used 305 for (uint32 i = safeMode ? 4 : 0; 306 i < sizeof(directories) / sizeof(directory_which); i++) { 307 BDirectory directory; 308 node_ref nodeRef; 309 BPath path; 310 if (find_directory(directories[i], &path) == B_OK 311 && path.Append("media") == B_OK 312 && directory.SetTo(path.Path()) == B_OK 313 && directory.GetNodeRef(&nodeRef) == B_OK) 314 fMonitorHandler->AddDirectory(&nodeRef); 315 } 316 317 #ifdef USER_ADDON_PATH 318 node_ref nodeRef; 319 if (entry.SetTo(USER_ADDON_PATH) == B_OK 320 && entry.GetNodeRef(&nodeRef) == B_OK) 321 fMonitorHandler->AddDirectory(&nodeRef); 322 #endif 323 324 fStartup = false; 325 326 InfoMap::iterator iterator = fInfoMap.begin(); 327 for (; iterator != fInfoMap.end(); iterator++) 328 _InstantiatePhysicalInputsAndOutputs(iterator->second); 329 330 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 331 _InstantiateAutostartFlavors(iterator->second); 332 333 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 334 _PutAddonIfPossible(iterator->second); 335 336 server_rescan_defaults_command cmd; 337 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 338 } 339 340 341 bool 342 MediaAddonServer::QuitRequested() 343 { 344 CALLED(); 345 346 InfoMap::iterator iterator = fInfoMap.begin(); 347 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 348 _DestroyInstantiatedFlavors(iterator->second); 349 350 BMediaRoster::CurrentRoster()->Lock(); 351 BMediaRoster::CurrentRoster()->Quit(); 352 353 for (iterator = fInfoMap.begin(); iterator != fInfoMap.end(); iterator++) 354 _PutAddonIfPossible(iterator->second); 355 356 return true; 357 } 358 359 360 void 361 MediaAddonServer::MessageReceived(BMessage* message) 362 { 363 switch (message->what) { 364 case MEDIA_ADD_ON_SERVER_PLAY_MEDIA: 365 { 366 const char* name; 367 const char* type; 368 if (message->FindString(MEDIA_NAME_KEY, &name) != B_OK 369 || message->FindString(MEDIA_TYPE_KEY, &type) != B_OK) { 370 message->SendReply(B_ERROR); 371 break; 372 } 373 374 PlayMediaFile(type, name); 375 message->SendReply((uint32)B_OK); 376 // TODO: don't know which reply is expected 377 return; 378 } 379 380 default: 381 BApplication::MessageReceived(message); 382 break; 383 } 384 } 385 386 387 void 388 MediaAddonServer::_HandleMessage(int32 code, const void* data, size_t size) 389 { 390 switch (code) { 391 case ADD_ON_SERVER_INSTANTIATE_DORMANT_NODE: 392 { 393 const add_on_server_instantiate_dormant_node_request* request 394 = static_cast< 395 const add_on_server_instantiate_dormant_node_request*>( 396 data); 397 add_on_server_instantiate_dormant_node_reply reply; 398 399 status_t status 400 = MediaRosterEx(fMediaRoster)->InstantiateDormantNode( 401 request->add_on_id, request->flavor_id, 402 request->creator_team, &reply.node); 403 request->SendReply(status, &reply, sizeof(reply)); 404 break; 405 } 406 407 case ADD_ON_SERVER_RESCAN_ADD_ON_FLAVORS: 408 { 409 const add_on_server_rescan_flavors_command* command = static_cast< 410 const add_on_server_rescan_flavors_command*>(data); 411 BMediaAddOn* addOn 412 = gDormantNodeManager->GetAddOn(command->add_on_id); 413 if (addOn == NULL) { 414 ERROR("rescan flavors: Can't find a addon object for id %d\n", 415 (int)command->add_on_id); 416 break; 417 } 418 _ScanAddOnFlavors(addOn); 419 gDormantNodeManager->PutAddOn(command->add_on_id); 420 break; 421 } 422 423 case ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY: 424 if (fStartupSound) { 425 system_beep(MEDIA_SOUNDS_STARTUP); 426 fStartupSound = false; 427 } 428 break; 429 430 default: 431 ERROR("media_addon_server: received unknown message code %#08lx\n", 432 code); 433 break; 434 } 435 } 436 437 438 status_t 439 MediaAddonServer::_ControlThread(void* _server) 440 { 441 MediaAddonServer* server = (MediaAddonServer*)_server; 442 443 char data[B_MEDIA_MESSAGE_SIZE]; 444 ssize_t size; 445 int32 code; 446 while ((size = read_port_etc(server->_ControlPort(), &code, data, 447 sizeof(data), 0, 0)) > 0) 448 server->_HandleMessage(code, data, size); 449 450 return B_OK; 451 } 452 453 454 void 455 MediaAddonServer::_ScanAddOnFlavors(BMediaAddOn* addon) 456 { 457 ASSERT(addon->AddonID() > 0); 458 459 TRACE("MediaAddonServer::_ScanAddOnFlavors: id %ld\n", addon->AddonID()); 460 461 // cache the media_addon_id in a local variable to avoid 462 // calling BMediaAddOn::AddonID() too often 463 media_addon_id addonID = addon->AddonID(); 464 465 // update the cached flavor count, get oldflavorcount and newflavorcount 466 467 InfoMap::iterator found = fInfoMap.find(addonID); 468 ASSERT(found != fInfoMap.end()); 469 470 AddOnInfo& info = found->second; 471 int32 oldFlavorCount = info.flavor_count; 472 int32 newFlavorCount = addon->CountFlavors(); 473 info.flavor_count = newFlavorCount; 474 475 TRACE("%ld old flavors, %ld new flavors\n", oldflavorcount, newFlavorCount); 476 477 // during the first update (i == 0), the server removes old dormant_flavor_infos 478 for (int i = 0; i < newFlavorCount; i++) { 479 const flavor_info* flavorInfo; 480 TRACE("flavor %d:\n", i); 481 if (addon->GetFlavorAt(i, &flavorInfo) != B_OK) { 482 ERROR("MediaAddonServer::_ScanAddOnFlavors GetFlavorAt failed for " 483 "index %d!\n", i); 484 continue; 485 } 486 487 #if DEBUG >= 2 488 DumpFlavorInfo(flavorInfo); 489 #endif 490 491 dormant_flavor_info dormantFlavorInfo; 492 dormantFlavorInfo = *flavorInfo; 493 dormantFlavorInfo.node_info.addon = addonID; 494 dormantFlavorInfo.node_info.flavor_id = flavorInfo->internal_id; 495 strlcpy(dormantFlavorInfo.node_info.name, flavorInfo->name, 496 B_MEDIA_NAME_LENGTH); 497 498 size_t flattenedSize = dormantFlavorInfo.FlattenedSize(); 499 size_t messageSize = flattenedSize 500 + sizeof(server_register_dormant_node_command); 501 server_register_dormant_node_command* message 502 = (server_register_dormant_node_command*)malloc(messageSize); 503 if (message == NULL) 504 break; 505 506 // The server should remove previously registered "dormant_flavor_info"s 507 // during the first update, but after the first iteration, we don't 508 // want the server to anymore remove old dormant_flavor_infos 509 message->purge_id = i == 0 ? addonID : 0; 510 511 message->type = dormantFlavorInfo.TypeCode(); 512 message->flattened_size = flattenedSize; 513 dormantFlavorInfo.Flatten(message->flattened_data, flattenedSize); 514 515 status_t status = SendToServer(SERVER_REGISTER_DORMANT_NODE, 516 message, messageSize); 517 if (status != B_OK) { 518 ERROR("MediaAddonServer::_ScanAddOnFlavors: couldn't register " 519 "dormant node: %s\n", strerror(status)); 520 } 521 522 free(message); 523 } 524 525 // TODO: we currently pretend that all old flavors have been removed, this 526 // could probably be done in a smarter way 527 BPrivate::media::notifications::FlavorsChanged(addonID, newFlavorCount, 528 oldFlavorCount); 529 } 530 531 532 void 533 MediaAddonServer::_AddOnAdded(const char* path, ino_t fileNode) 534 { 535 TRACE("\n\nMediaAddonServer::_AddOnAdded: path %s\n", path); 536 537 media_addon_id id = gDormantNodeManager->RegisterAddOn(path); 538 if (id <= 0) { 539 ERROR("MediaAddonServer::_AddOnAdded: failed to register add-on %s\n", 540 path); 541 return; 542 } 543 544 TRACE("MediaAddonServer::_AddOnAdded: loading addon %ld now...\n", id); 545 546 BMediaAddOn* addon = gDormantNodeManager->GetAddOn(id); 547 if (addon == NULL) { 548 ERROR("MediaAddonServer::_AddOnAdded: failed to get add-on %s\n", path); 549 gDormantNodeManager->UnregisterAddOn(id); 550 return; 551 } 552 553 TRACE("MediaAddonServer::_AddOnAdded: loading finished, id %ld\n", id); 554 555 try { 556 // put file's inode and addon's id into map 557 fFileMap.insert(std::make_pair(fileNode, id)); 558 559 AddOnInfo info; 560 fInfoMap.insert(std::make_pair(id, info)); 561 } catch (std::bad_alloc& exception) { 562 fFileMap.erase(fileNode); 563 return; 564 } 565 566 InfoMap::iterator found = fInfoMap.find(id); 567 AddOnInfo& info = found->second; 568 569 info.id = id; 570 info.wants_autostart = false; // temporary default 571 info.flavor_count = 0; 572 info.addon = addon; 573 574 // scan the flavors 575 _ScanAddOnFlavors(addon); 576 577 // need to call BMediaNode::WantsAutoStart() 578 // after the flavors have been scanned 579 info.wants_autostart = addon->WantsAutoStart(); 580 581 if (info.wants_autostart) 582 TRACE("add-on %ld WantsAutoStart!\n", id); 583 584 // During startup, first all add-ons are loaded, then all 585 // nodes (flavors) representing physical inputs and outputs 586 // are instantiated. Next, all add-ons that need autostart 587 // will be autostarted. Finally, add-ons that don't have 588 // any active nodes (flavors) will be unloaded. 589 590 // After startup is done, we simply do it for each new 591 // loaded add-on, too. 592 if (!fStartup) { 593 _InstantiatePhysicalInputsAndOutputs(info); 594 _InstantiateAutostartFlavors(info); 595 _PutAddonIfPossible(info); 596 597 // since something might have changed 598 server_rescan_defaults_command cmd; 599 SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); 600 } 601 602 // we do not call gDormantNodeManager->PutAddOn(id) 603 // since it is done by _PutAddonIfPossible() 604 } 605 606 607 void 608 MediaAddonServer::_DestroyInstantiatedFlavors(AddOnInfo& info) 609 { 610 printf("MediaAddonServer::_DestroyInstantiatedFlavors addon %ld\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 %ld\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 %ld!\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 PRINT("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 %ld, " 732 "name %s\n", flavorinfo->internal_id, flavorinfo->name); 733 } else { 734 PRINT("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 PRINT("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 %ld\n", index); 762 763 status = MediaRosterEx(fMediaRoster)->RegisterNode(node, info.id, 764 internalID); 765 if (status != B_OK) { 766 ERROR("failed to register node %ld\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 %Ld 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 %ld\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 "%ld 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