1 /* 2 * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files or portions 6 * thereof (the "Software"), to deal in the Software without restriction, 7 * including without limitation the rights to use, copy, modify, merge, 8 * publish, distribute, sublicense, and/or sell copies of the Software, 9 * and to permit persons to whom the Software is furnished to do so, subject 10 * to the following conditions: 11 * 12 * * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * * Redistributions in binary form must reproduce the above copyright notice 16 * in the binary, as well as this list of conditions and the following 17 * disclaimer in the documentation and/or other materials provided with 18 * the distribution. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 * THE SOFTWARE. 27 * 28 */ 29 30 31 #include "NodeManager.h" 32 33 #include <Autolock.h> 34 #include <Entry.h> 35 #include <MediaAddOn.h> 36 #include <MediaDefs.h> 37 #include <Message.h> 38 #include <Messenger.h> 39 #include <OS.h> 40 #include <Path.h> 41 42 #include <debug.h> 43 #include <MediaMisc.h> 44 45 #include "AppManager.h" 46 #include "DefaultManager.h" 47 #include "media_server.h" 48 49 50 const char* 51 get_node_type(node_type type) 52 { 53 #define CASE(c) case c: return #c; 54 switch (type) { 55 CASE(VIDEO_INPUT) 56 CASE(AUDIO_INPUT) 57 CASE(VIDEO_OUTPUT) 58 CASE(AUDIO_MIXER) 59 CASE(AUDIO_OUTPUT) 60 CASE(AUDIO_OUTPUT_EX) 61 CASE(TIME_SOURCE) 62 CASE(SYSTEM_TIME_SOURCE) 63 64 default: 65 return "unknown"; 66 } 67 #undef CASE 68 } 69 70 71 // #pragma mark - 72 73 74 NodeManager::NodeManager() 75 : 76 BLocker("node manager"), 77 fNextAddOnID(1), 78 fNextNodeID(1), 79 fDefaultManager(new DefaultManager) 80 { 81 } 82 83 84 NodeManager::~NodeManager() 85 { 86 delete fDefaultManager; 87 } 88 89 90 // #pragma mark - Default node management 91 92 93 status_t 94 NodeManager::SetDefaultNode(node_type type, const media_node* node, 95 const dormant_node_info* info, const media_input* input) 96 { 97 BAutolock _(this); 98 99 status_t status = B_BAD_VALUE; 100 if (node != NULL) 101 status = fDefaultManager->Set(node->node, NULL, 0, type); 102 else if (input != NULL) { 103 status = fDefaultManager->Set(input->node.node, input->name, 104 input->destination.id, type); 105 } else if (info != NULL) { 106 media_node_id nodeID; 107 int32 count = 1; 108 status = GetInstances(info->addon, info->flavor_id, &nodeID, &count, 109 count); 110 if (status == B_OK) 111 status = fDefaultManager->Set(nodeID, NULL, 0, type); 112 } 113 114 if (status == B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT 115 || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) { 116 fDefaultManager->SaveState(this); 117 Dump(); 118 } 119 return status; 120 } 121 122 123 status_t 124 NodeManager::GetDefaultNode(node_type type, media_node_id* _nodeID, 125 char* inputName, int32* _inputID) 126 { 127 BAutolock _(this); 128 return fDefaultManager->Get(_nodeID, inputName, _inputID, type); 129 } 130 131 132 status_t 133 NodeManager::RescanDefaultNodes() 134 { 135 BAutolock _(this); 136 return fDefaultManager->Rescan(); 137 } 138 139 140 // #pragma mark - Live node management 141 142 143 status_t 144 NodeManager::RegisterNode(media_addon_id addOnID, int32 flavorID, 145 const char* name, uint64 kinds, port_id port, team_id team, 146 media_node_id* _nodeID) 147 { 148 BAutolock _(this); 149 150 registered_node node; 151 node.node_id = fNextNodeID; 152 node.add_on_id = addOnID; 153 node.flavor_id = flavorID; 154 strlcpy(node.name, name, sizeof(node.name)); 155 node.kinds = kinds; 156 node.port = port; 157 node.containing_team = team; 158 node.creator = -1; // will be set later 159 node.ref_count = 1; 160 161 try { 162 node.team_ref_count.insert(std::make_pair(team, 1)); 163 164 fNodeMap.insert(std::make_pair(fNextNodeID, node)); 165 } catch (std::bad_alloc& exception) { 166 return B_NO_MEMORY; 167 } 168 169 *_nodeID = fNextNodeID++; 170 171 TRACE("NodeManager::RegisterNode: node %" B_PRId32 ", addon_id %" B_PRId32 172 ", flavor_id %" B_PRId32 ", name \"%s\", kinds %#Lx, port %" B_PRId32 173 ", team %" B_PRId32 "\n", *_nodeID, addOnID, flavorID, name, kinds, 174 port, team); 175 return B_OK; 176 } 177 178 179 status_t 180 NodeManager::UnregisterNode(media_node_id id, team_id team, 181 media_addon_id* _addOnID, int32* _flavorID) 182 { 183 TRACE("NodeManager::UnregisterNode enter: node %" B_PRId32 ", team %" 184 B_PRId32 "\n", id, team); 185 186 BAutolock _(this); 187 188 NodeMap::iterator found = fNodeMap.find(id); 189 if (found == fNodeMap.end()) { 190 ERROR("NodeManager::UnregisterNode: couldn't find node %" B_PRId32 191 " (team %" B_PRId32 ")\n", id, team); 192 return B_ERROR; 193 } 194 195 registered_node& node = found->second; 196 197 if (node.containing_team != team) { 198 ERROR("NodeManager::UnregisterNode: team %" B_PRId32 " tried to " 199 "unregister node %" B_PRId32 ", but it was instantiated by team %" 200 B_PRId32 "\n", team, id, node.containing_team); 201 return B_ERROR; 202 } 203 if (node.ref_count != 1) { 204 ERROR("NodeManager::UnregisterNode: node %" B_PRId32 ", team %" 205 B_PRId32 " has ref count %" B_PRId32 " (should be 1)\n", id, team, 206 node.ref_count); 207 //return B_ERROR; 208 } 209 210 *_addOnID = node.add_on_id; 211 *_flavorID = node.flavor_id; 212 213 fNodeMap.erase(found); 214 215 TRACE("NodeManager::UnregisterNode leave: node %" B_PRId32 ", addon_id %" 216 B_PRId32 ", flavor_id %" B_PRId32 " team %" B_PRId32 "\n", id, 217 *_addOnID, *_flavorID, team); 218 return B_OK; 219 } 220 221 222 status_t 223 NodeManager::ReleaseNodeReference(media_node_id id, team_id team) 224 { 225 TRACE("NodeManager::ReleaseNodeReference enter: node %" B_PRId32 ", team %" 226 B_PRId32 "\n", id, team); 227 228 BAutolock _(this); 229 230 NodeMap::iterator found = fNodeMap.find(id); 231 if (found == fNodeMap.end()) { 232 ERROR("NodeManager::ReleaseNodeReference: node %" B_PRId32 " not " 233 "found\n", id); 234 return B_ERROR; 235 } 236 237 registered_node& node = found->second; 238 239 TeamCountMap::iterator teamRef = node.team_ref_count.find(team); 240 if (teamRef == node.team_ref_count.end()) { 241 // Normally it is an error to release a node in another team. But we 242 // make one exception: if the node is global, and the creator team 243 // tries to release it, we will release it in the the 244 // media_addon_server. 245 team_id addOnServer = gAppManager->AddOnServerTeam(); 246 teamRef = node.team_ref_count.find(addOnServer); 247 248 if (node.creator == team && teamRef != node.team_ref_count.end()) { 249 PRINT(1, "!!! NodeManager::ReleaseNodeReference doing global " 250 "release!\n"); 251 node.creator = -1; // invalidate! 252 team = addOnServer; 253 } else { 254 ERROR("NodeManager::ReleaseNodeReference: node %" B_PRId32 " has " 255 "no team %" B_PRId32 " references\n", id, team); 256 return B_ERROR; 257 } 258 } 259 260 #if DEBUG 261 int32 teamCount = teamRef->second - 1; 262 (void)teamCount; 263 #endif 264 265 if (--teamRef->second == 0) 266 node.team_ref_count.erase(teamRef); 267 268 if (--node.ref_count == 0) { 269 PRINT(1, "NodeManager::ReleaseNodeReference: detected released node is" 270 " now unused, node %" B_PRId32 "\n", id); 271 272 // TODO: remove! 273 node_final_release_command command; 274 status_t status = SendToPort(node.port, NODE_FINAL_RELEASE, &command, 275 sizeof(command)); 276 if (status != B_OK) { 277 ERROR("NodeManager::ReleaseNodeReference: can't send command to " 278 "node %" B_PRId32 "\n", id); 279 // ignore error 280 } 281 } 282 283 TRACE("NodeManager::ReleaseNodeReference leave: node %" B_PRId32 ", team %" 284 B_PRId32 ", ref %" B_PRId32 ", team ref %" B_PRId32 "\n", id, team, 285 node.ref_count, teamCount); 286 return B_OK; 287 } 288 289 290 status_t 291 NodeManager::ReleaseNodeAll(media_node_id id) 292 { 293 TRACE("NodeManager::ReleaseNodeAll enter: node %" B_PRId32 "\n", id); 294 295 BAutolock _(this); 296 297 NodeMap::iterator found = fNodeMap.find(id); 298 if (found == fNodeMap.end()) { 299 ERROR("NodeManager::ReleaseNodeAll: node %" B_PRId32 " not found\n", 300 id); 301 return B_ERROR; 302 } 303 304 registered_node& node = found->second; 305 node.team_ref_count.clear(); 306 node.ref_count = 0; 307 308 node_final_release_command command; 309 status_t status = SendToPort(node.port, NODE_FINAL_RELEASE, &command, 310 sizeof(command)); 311 if (status != B_OK) { 312 ERROR("NodeManager::ReleaseNodeAll: can't send command to " 313 "node %" B_PRId32 "\n", id); 314 // ignore error 315 } 316 317 TRACE("NodeManager::ReleaseNodeAll leave: node %" B_PRId32 "\n", id); 318 return B_OK; 319 } 320 321 322 status_t 323 NodeManager::SetNodeCreator(media_node_id id, team_id creator) 324 { 325 TRACE("NodeManager::SetNodeCreator node %" B_PRId32 ", creator %" B_PRId32 326 "\n", id, creator); 327 328 BAutolock _(this); 329 330 NodeMap::iterator found = fNodeMap.find(id); 331 if (found == fNodeMap.end()) { 332 ERROR("NodeManager::SetNodeCreator: node %" B_PRId32 " not found\n", 333 id); 334 return B_ERROR; 335 } 336 337 registered_node& node = found->second; 338 339 if (node.creator != -1) { 340 ERROR("NodeManager::SetNodeCreator: node %" B_PRId32 " is already" 341 " assigned creator %" B_PRId32 "\n", id, node.creator); 342 return B_ERROR; 343 } 344 345 node.creator = creator; 346 return B_OK; 347 } 348 349 350 status_t 351 NodeManager::GetCloneForID(media_node_id id, team_id team, media_node* node) 352 { 353 TRACE("NodeManager::GetCloneForID enter: node %" B_PRId32 " team %" 354 B_PRId32 "\n", id, team); 355 356 BAutolock _(this); 357 358 status_t status = _AcquireNodeReference(id, team); 359 if (status != B_OK) { 360 ERROR("NodeManager::GetCloneForID: couldn't increment ref count, " 361 "node %" B_PRId32 " team %" B_PRId32 "\n", id, team); 362 return status; 363 } 364 365 NodeMap::iterator found = fNodeMap.find(id); 366 if (found == fNodeMap.end()) { 367 ERROR("NodeManager::GetCloneForID: node %" B_PRId32 " not found\n", 368 id); 369 return B_ERROR; 370 } 371 372 registered_node& registeredNode = found->second; 373 374 node->node = registeredNode.node_id; 375 node->port = registeredNode.port; 376 node->kind = registeredNode.kinds; 377 378 TRACE("NodeManager::GetCloneForID leave: node %" B_PRId32 " team %" 379 B_PRId32 "\n", id, team); 380 return B_OK; 381 } 382 383 384 /*! This function locates the default "node" for the requested "type" and 385 returns a clone. 386 If the requested type is AUDIO_OUTPUT_EX, also "input_name" and "input_id" 387 need to be set and returned, as this is required by 388 BMediaRoster::GetAudioOutput(media_node *out_node, int32 *out_input_id, 389 BString *out_input_name). 390 */ 391 status_t 392 NodeManager::GetClone(node_type type, team_id team, media_node* node, 393 char* inputName, int32* _inputID) 394 { 395 BAutolock _(this); 396 397 TRACE("NodeManager::GetClone enter: team %" B_PRId32 ", type %d (%s)\n", 398 team, type, get_node_type(type)); 399 400 media_node_id id; 401 status_t status = GetDefaultNode(type, &id, inputName, _inputID); 402 if (status != B_OK) { 403 ERROR("NodeManager::GetClone: couldn't GetDefaultNode, team %" B_PRId32 404 ", type %d (%s)\n", team, type, get_node_type(type)); 405 *node = media_node::null; 406 return status; 407 } 408 ASSERT(id > 0); 409 410 status = GetCloneForID(id, team, node); 411 if (status != B_OK) { 412 ERROR("NodeManager::GetClone: couldn't GetCloneForID, id %" B_PRId32 413 ", team %" B_PRId32 ", type %d (%s)\n", id, team, type, 414 get_node_type(type)); 415 *node = media_node::null; 416 return status; 417 } 418 ASSERT(id == node->node); 419 420 TRACE("NodeManager::GetClone leave: node id %" B_PRId32 ", node port %" 421 B_PRId32 ", node kind %#lx\n", node->node, node->port, node->kind); 422 return B_OK; 423 } 424 425 426 status_t 427 NodeManager::ReleaseNode(const media_node& node, team_id team) 428 { 429 TRACE("NodeManager::ReleaseNode enter: node %" B_PRId32 " team %" B_PRId32 430 "\n", node.node, team); 431 432 if (ReleaseNodeReference(node.node, team) != B_OK) { 433 ERROR("NodeManager::ReleaseNode: couldn't decrement node %" B_PRId32 434 " team %" B_PRId32 " ref count\n", node.node, team); 435 } 436 437 return B_OK; 438 } 439 440 441 status_t 442 NodeManager::PublishInputs(const media_node& node, const media_input* inputs, 443 int32 count) 444 { 445 BAutolock _(this); 446 447 NodeMap::iterator found = fNodeMap.find(node.node); 448 if (found == fNodeMap.end()) { 449 ERROR("NodeManager::PublishInputs: node %" B_PRId32 " not found\n", 450 node.node); 451 return B_ERROR; 452 } 453 454 registered_node& registeredNode = found->second; 455 456 registeredNode.input_list.clear(); 457 458 try { 459 for (int32 i = 0; i < count; i++) 460 registeredNode.input_list.push_back(inputs[i]); 461 } catch (std::bad_alloc& exception) { 462 return B_NO_MEMORY; 463 } 464 465 return B_OK; 466 } 467 468 469 status_t 470 NodeManager::PublishOutputs(const media_node &node, const media_output* outputs, 471 int32 count) 472 { 473 BAutolock _(this); 474 475 NodeMap::iterator found = fNodeMap.find(node.node); 476 if (found == fNodeMap.end()) { 477 ERROR("NodeManager::PublishOutputs: node %" B_PRId32 " not found\n", 478 node.node); 479 return B_ERROR; 480 } 481 482 registered_node& registeredNode = found->second; 483 484 registeredNode.output_list.clear(); 485 486 try { 487 for (int32 i = 0; i < count; i++) 488 registeredNode.output_list.push_back(outputs[i]); 489 } catch (std::bad_alloc& exception) { 490 return B_NO_MEMORY; 491 } 492 493 return B_OK; 494 } 495 496 497 status_t 498 NodeManager::FindNodeID(port_id port, media_node_id* _id) 499 { 500 BAutolock _(this); 501 502 NodeMap::iterator iterator = fNodeMap.begin(); 503 for (; iterator != fNodeMap.end(); iterator++) { 504 registered_node& node = iterator->second; 505 506 if (node.port == port) { 507 *_id = node.node_id; 508 TRACE("NodeManager::FindNodeID found port %" B_PRId32 ", node %" 509 B_PRId32 "\n", port, node.node_id); 510 return B_OK; 511 } 512 513 OutputList::iterator outIterator = node.output_list.begin(); 514 for (; outIterator != node.output_list.end(); outIterator++) { 515 if (outIterator->source.port == port) { 516 *_id = node.node_id; 517 TRACE("NodeManager::FindNodeID found output port %" B_PRId32 518 ", node %" B_PRId32 "\n", port, node.node_id); 519 return B_OK; 520 } 521 } 522 523 InputList::iterator inIterator = node.input_list.begin(); 524 for (; inIterator != node.input_list.end(); inIterator++) { 525 if (inIterator->destination.port == port) { 526 *_id = node.node_id; 527 TRACE("NodeManager::FindNodeID found input port %" B_PRId32 528 ", node %" B_PRId32 "\n", port, node.node_id); 529 return B_OK; 530 } 531 } 532 } 533 534 ERROR("NodeManager::FindNodeID failed, port %" B_PRId32 "\n", port); 535 return B_ERROR; 536 } 537 538 539 status_t 540 NodeManager::GetDormantNodeInfo(const media_node& node, 541 dormant_node_info* nodeInfo) 542 { 543 // TODO: not sure if this is correct 544 BAutolock _(this); 545 546 NodeMap::iterator found = fNodeMap.find(node.node); 547 if (found == fNodeMap.end()) { 548 ERROR("NodeManager::GetDormantNodeInfo: node %" B_PRId32 " not found" 549 "\n", node.node); 550 return B_ERROR; 551 } 552 553 registered_node& registeredNode = found->second; 554 555 if (registeredNode.add_on_id == -1 556 && node.node != NODE_SYSTEM_TIMESOURCE_ID) { 557 // This function must return an error if the node is application owned 558 TRACE("NodeManager::GetDormantNodeInfo NODE IS APPLICATION OWNED! " 559 "node %" B_PRId32 ", add_on_id %" B_PRId32 ", flavor_id %" B_PRId32 560 ", name \"%s\"\n", node.node, registeredNode.add_on_id, 561 registeredNode.flavor_id, registeredNode.name); 562 return B_ERROR; 563 } 564 565 ASSERT(node.port == registeredNode.port); 566 ASSERT((node.kind & NODE_KIND_COMPARE_MASK) 567 == (registeredNode.kinds & NODE_KIND_COMPARE_MASK)); 568 569 nodeInfo->addon = registeredNode.add_on_id; 570 nodeInfo->flavor_id = registeredNode.flavor_id; 571 strlcpy(nodeInfo->name, registeredNode.name, sizeof(nodeInfo->name)); 572 573 TRACE("NodeManager::GetDormantNodeInfo node %" B_PRId32 ", add_on_id %" 574 B_PRId32 ", flavor_id %" B_PRId32 ", name \"%s\"\n", node.node, 575 registeredNode.add_on_id, registeredNode.flavor_id, 576 registeredNode.name); 577 return B_OK; 578 } 579 580 581 status_t 582 NodeManager::GetLiveNodeInfo(const media_node& node, live_node_info* liveInfo) 583 { 584 BAutolock _(this); 585 586 NodeMap::iterator found = fNodeMap.find(node.node); 587 if (found == fNodeMap.end()) { 588 ERROR("NodeManager::GetLiveNodeInfo: node %" B_PRId32 " not found\n", 589 node.node); 590 return B_ERROR; 591 } 592 593 registered_node& registeredNode = found->second; 594 595 ASSERT(node.port == registeredNode.port); 596 ASSERT((node.kind & NODE_KIND_COMPARE_MASK) 597 == (registeredNode.kinds & NODE_KIND_COMPARE_MASK)); 598 599 liveInfo->node = node; 600 liveInfo->hint_point = BPoint(0, 0); 601 strlcpy(liveInfo->name, registeredNode.name, sizeof(liveInfo->name)); 602 603 TRACE("NodeManager::GetLiveNodeInfo node %" B_PRId32 ", name = \"%s\"\n", 604 node.node, registeredNode.name); 605 return B_OK; 606 } 607 608 609 status_t 610 NodeManager::GetInstances(media_addon_id addOnID, int32 flavorID, 611 media_node_id* ids, int32* _count, int32 maxCount) 612 { 613 BAutolock _(this); 614 615 NodeMap::iterator iterator = fNodeMap.begin(); 616 int32 count = 0; 617 for (; iterator != fNodeMap.end() && count < maxCount; iterator++) { 618 registered_node& node = iterator->second; 619 620 if (node.add_on_id == addOnID && node.flavor_id == flavorID) 621 ids[count++] = node.node_id; 622 } 623 624 TRACE("NodeManager::GetInstances found %" B_PRId32 " instances for " 625 "addon_id %" B_PRId32 ", flavor_id %" B_PRId32 "\n", count, addOnID, 626 flavorID); 627 *_count = count; 628 return B_OK; 629 } 630 631 632 status_t 633 NodeManager::GetLiveNodes(LiveNodeList& liveNodes, int32 maxCount, 634 const media_format* inputFormat, const media_format* outputFormat, 635 const char* name, uint64 requireKinds) 636 { 637 TRACE("NodeManager::GetLiveNodes: maxCount %" B_PRId32 ", in-format %p, " 638 "out-format %p, name %s, require kinds 0x%" B_PRIx64 "\n", maxCount, 639 inputFormat, outputFormat, name != NULL ? name : "NULL", requireKinds); 640 641 BAutolock _(this); 642 643 // Determine the count of byte to compare when checking for a name with 644 // or without wildcard 645 size_t nameLength = 0; 646 if (name != NULL) { 647 nameLength = strlen(name); 648 if (nameLength > 0 && name[nameLength - 1] == '*') 649 nameLength--; 650 } 651 652 NodeMap::iterator iterator = fNodeMap.begin(); 653 int32 count = 0; 654 for (; iterator != fNodeMap.end() && count < maxCount; iterator++) { 655 registered_node& node = iterator->second; 656 657 if ((node.kinds & requireKinds) != requireKinds) 658 continue; 659 660 if (nameLength != 0) { 661 if (strncmp(name, node.name, nameLength) != 0) 662 continue; 663 } 664 665 if (inputFormat != NULL) { 666 bool found = false; 667 668 for (InputList::iterator inIterator = node.input_list.begin(); 669 inIterator != node.input_list.end(); inIterator++) { 670 media_input& input = *inIterator; 671 672 if (format_is_compatible(*inputFormat, input.format)) { 673 found = true; 674 break; 675 } 676 } 677 678 if (!found) 679 continue; 680 } 681 682 if (outputFormat != NULL) { 683 bool found = false; 684 685 for (OutputList::iterator outIterator = node.output_list.begin(); 686 outIterator != node.output_list.end(); outIterator++) { 687 media_output& output = *outIterator; 688 689 if (format_is_compatible(*outputFormat, output.format)) { 690 found = true; 691 break; 692 } 693 } 694 695 if (!found) 696 continue; 697 } 698 699 live_node_info info; 700 info.node.node = node.node_id; 701 info.node.port = node.port; 702 info.node.kind = node.kinds; 703 info.hint_point = BPoint(0, 0); 704 strlcpy(info.name, node.name, sizeof(info.name)); 705 706 try { 707 liveNodes.push_back(info); 708 } catch (std::bad_alloc& exception) { 709 return B_NO_MEMORY; 710 } 711 712 count++; 713 } 714 715 TRACE("NodeManager::GetLiveNodes found %" B_PRId32 "\n", count); 716 return B_OK; 717 } 718 719 720 /*! Add media_node_id of all live nodes to the message 721 int32 "media_node_id" (multiple items) 722 */ 723 status_t 724 NodeManager::GetLiveNodes(BMessage* message) 725 { 726 BAutolock _(this); 727 728 NodeMap::iterator iterator = fNodeMap.begin(); 729 for (; iterator != fNodeMap.end(); iterator++) { 730 registered_node& node = iterator->second; 731 732 if (message->AddInt32("media_node_id", node.node_id) != B_OK) 733 return B_NO_MEMORY; 734 } 735 736 return B_OK; 737 } 738 739 740 // #pragma mark - Registration of BMediaAddOns 741 742 743 void 744 NodeManager::RegisterAddOn(const entry_ref& ref, media_addon_id* _newID) 745 { 746 BAutolock _(this); 747 748 media_addon_id id = fNextAddOnID++; 749 750 // printf("NodeManager::RegisterAddOn: ref-name \"%s\", assigning id %" 751 // B_PRId32 "\n", ref.name, id); 752 753 try { 754 fPathMap.insert(std::make_pair(id, ref)); 755 *_newID = id; 756 } catch (std::bad_alloc& exception) { 757 *_newID = -1; 758 } 759 } 760 761 762 void 763 NodeManager::UnregisterAddOn(media_addon_id addOnID) 764 { 765 PRINT(1, "NodeManager::UnregisterAddOn: id %" B_PRId32 "\n", addOnID); 766 767 BAutolock _(this); 768 769 RemoveDormantFlavorInfo(addOnID); 770 fPathMap.erase(addOnID); 771 } 772 773 774 status_t 775 NodeManager::GetAddOnRef(media_addon_id addOnID, entry_ref* ref) 776 { 777 BAutolock _(this); 778 779 PathMap::iterator found = fPathMap.find(addOnID); 780 if (found == fPathMap.end()) 781 return B_ERROR; 782 783 *ref = found->second; 784 return B_OK; 785 } 786 787 788 // #pragma mark - Registration of node flavors, published by BMediaAddOns 789 790 791 //! This function is only used (indirectly) by the media_addon_server. 792 status_t 793 NodeManager::AddDormantFlavorInfo(const dormant_flavor_info& flavorInfo) 794 { 795 PRINT(1, "NodeManager::AddDormantFlavorInfo, addon-id %" B_PRId32 ", " 796 "flavor-id %" B_PRId32 ", name \"%s\", flavor-name \"%s\", flavor-info" 797 " \"%s\"\n", flavorInfo.node_info.addon, 798 flavorInfo.node_info.flavor_id, flavorInfo.node_info.name, 799 flavorInfo.name, flavorInfo.info); 800 801 BAutolock _(this); 802 803 // Try to find the addon-id/flavor-id in the list. 804 // If it already exists, update the info, but don't change its instance 805 // count. 806 807 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 808 iterator != fDormantFlavors.end(); iterator++) { 809 dormant_add_on_flavor_info& info = *iterator; 810 811 if (info.add_on_id != flavorInfo.node_info.addon 812 || info.flavor_id != flavorInfo.node_info.flavor_id) 813 continue; 814 815 if (info.info_valid) { 816 ERROR("NodeManager::AddDormantFlavorInfo, addon-id %" B_PRId32 ", " 817 "flavor-id %" B_PRId32 " does already exist\n", 818 info.info.node_info.addon, info.info.node_info.flavor_id); 819 } 820 821 TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %" B_PRId32 822 ", flavor-id %" B_PRId32 "\n", info.info.node_info.addon, 823 info.info.node_info.flavor_id); 824 825 info.max_instances_count = flavorInfo.possible_count > 0 826 ? flavorInfo.possible_count : INT32_MAX; 827 info.info_valid = true; 828 info.info = flavorInfo; 829 return B_OK; 830 } 831 832 // Insert information into the list 833 834 dormant_add_on_flavor_info info; 835 info.add_on_id = flavorInfo.node_info.addon; 836 info.flavor_id = flavorInfo.node_info.flavor_id; 837 info.max_instances_count = flavorInfo.possible_count > 0 838 ? flavorInfo.possible_count : INT32_MAX; 839 info.instances_count = 0; 840 info.info_valid = true; 841 info.info = flavorInfo; 842 843 try { 844 fDormantFlavors.push_back(info); 845 } catch (std::bad_alloc& exception) { 846 return B_NO_MEMORY; 847 } 848 849 return B_OK; 850 } 851 852 853 //! This function is only used (indirectly) by the media_addon_server 854 void 855 NodeManager::InvalidateDormantFlavorInfo(media_addon_id addOnID) 856 { 857 BAutolock _(this); 858 859 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 860 iterator != fDormantFlavors.end(); iterator++) { 861 dormant_add_on_flavor_info& info = *iterator; 862 863 if (info.add_on_id == addOnID && info.info_valid) { 864 PRINT(1, "NodeManager::InvalidateDormantFlavorInfo, addon-id %" 865 B_PRId32 ", flavor-id %" B_PRId32 ", name \"%s\", flavor-name " 866 "\"%s\", flavor-info \"%s\"\n", info.info.node_info.addon, 867 info.info.node_info.flavor_id, info.info.node_info.name, 868 info.info.name, info.info.info); 869 870 info.info_valid = false; 871 } 872 } 873 } 874 875 876 //! This function is only used (indirectly) by the media_addon_server 877 void 878 NodeManager::RemoveDormantFlavorInfo(media_addon_id addOnID) 879 { 880 BAutolock _(this); 881 882 for (size_t index = 0; index < fDormantFlavors.size(); index++) { 883 dormant_add_on_flavor_info& info = fDormantFlavors[index]; 884 885 if (info.add_on_id == addOnID) { 886 PRINT(1, "NodeManager::RemoveDormantFlavorInfo, addon-id %" 887 B_PRId32 ", flavor-id %" B_PRId32 ", name \"%s\", flavor-name " 888 "\"%s\", flavor-info \"%s\"\n", info.info.node_info.addon, 889 info.info.node_info.flavor_id, info.info.node_info.name, 890 info.info.name, info.info.info); 891 fDormantFlavors.erase(fDormantFlavors.begin() + index--); 892 } 893 } 894 } 895 896 897 status_t 898 NodeManager::IncrementFlavorInstancesCount(media_addon_id addOnID, 899 int32 flavorID, team_id team) 900 { 901 BAutolock _(this); 902 903 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 904 iterator != fDormantFlavors.end(); iterator++) { 905 dormant_add_on_flavor_info& info = *iterator; 906 907 if (info.add_on_id != addOnID || info.flavor_id != flavorID) 908 continue; 909 910 if (info.instances_count >= info.max_instances_count) { 911 // maximum (or more) instances already exist 912 ERROR("NodeManager::IncrementFlavorInstancesCount addon-id %" 913 B_PRId32 ", flavor-id %" B_PRId32 " maximum (or more) " 914 "instances already exist\n", addOnID, flavorID); 915 return B_ERROR; 916 } 917 918 TeamCountMap::iterator teamInstance 919 = info.team_instances_count.find(team); 920 if (teamInstance == info.team_instances_count.end()) { 921 // This is the team's first instance 922 try { 923 info.team_instances_count.insert(std::make_pair(team, 1)); 924 } catch (std::bad_alloc& exception) { 925 return B_NO_MEMORY; 926 } 927 } else { 928 // Just increase its ref count 929 teamInstance->second++; 930 } 931 932 info.instances_count++; 933 return B_OK; 934 } 935 936 ERROR("NodeManager::IncrementFlavorInstancesCount addon-id %" B_PRId32 ", " 937 "flavor-id %" B_PRId32 " not found\n", addOnID, flavorID); 938 return B_ERROR; 939 } 940 941 942 status_t 943 NodeManager::DecrementFlavorInstancesCount(media_addon_id addOnID, 944 int32 flavorID, team_id team) 945 { 946 BAutolock _(this); 947 948 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 949 iterator != fDormantFlavors.end(); iterator++) { 950 dormant_add_on_flavor_info& info = *iterator; 951 952 if (info.add_on_id != addOnID || info.flavor_id != flavorID) 953 continue; 954 955 TeamCountMap::iterator teamInstance 956 = info.team_instances_count.find(team); 957 if (teamInstance == info.team_instances_count.end()) { 958 ERROR("NodeManager::DecrementFlavorInstancesCount addon-id %" 959 B_PRId32 ", flavor-id %" B_PRId32 " team %" B_PRId32 " has no " 960 "references\n", addOnID, flavorID, team); 961 return B_ERROR; 962 } 963 if (--teamInstance->second == 0) 964 info.team_instances_count.erase(teamInstance); 965 966 info.instances_count--; 967 return B_OK; 968 } 969 970 ERROR("NodeManager::DecrementFlavorInstancesCount addon-id %" B_PRId32 ", " 971 "flavor-id %" B_PRId32 " not found\n", addOnID, flavorID); 972 return B_ERROR; 973 } 974 975 976 //! This function is called when the media_addon_server has crashed 977 void 978 NodeManager::CleanupDormantFlavorInfos() 979 { 980 PRINT(1, "NodeManager::CleanupDormantFlavorInfos\n"); 981 982 BAutolock _(this); 983 fDormantFlavors.clear(); 984 // TODO: FlavorsChanged() notification 985 986 PRINT(1, "NodeManager::CleanupDormantFlavorInfos done\n"); 987 } 988 989 990 status_t 991 NodeManager::GetDormantNodes(dormant_node_info* infos, int32* _count, 992 const media_format* input, const media_format* output, const char* name, 993 uint64 requireKinds, uint64 denyKinds) 994 { 995 BAutolock _(this); 996 997 // Determine the count of byte to compare when checking for a name with 998 // or without wildcard 999 size_t nameLength = 0; 1000 if (name != NULL) { 1001 nameLength = strlen(name); 1002 if (nameLength > 0 && name[nameLength - 1] == '*') 1003 nameLength--; 1004 } 1005 1006 int32 maxCount = *_count; 1007 int32 count = 0; 1008 1009 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 1010 iterator != fDormantFlavors.end() && count < maxCount; iterator++) { 1011 dormant_add_on_flavor_info& info = *iterator; 1012 1013 if (!info.info_valid) 1014 continue; 1015 1016 if ((info.info.kinds & requireKinds) != requireKinds 1017 || (info.info.kinds & denyKinds) != 0) 1018 continue; 1019 1020 if (nameLength != 0) { 1021 if (strncmp(name, info.info.name, nameLength) != 0) 1022 continue; 1023 } 1024 1025 if (input != NULL) { 1026 bool found = false; 1027 1028 for (int32 i = 0; i < info.info.in_format_count; i++) { 1029 if (format_is_compatible(*input, info.info.in_formats[i])) { 1030 found = true; 1031 break; 1032 } 1033 } 1034 1035 if (!found) 1036 continue; 1037 } 1038 1039 if (output != NULL) { 1040 bool found = false; 1041 1042 for (int32 i = 0; i < info.info.out_format_count; i++) { 1043 if (format_is_compatible(*output, info.info.out_formats[i])) { 1044 found = true; 1045 break; 1046 } 1047 } 1048 1049 if (!found) 1050 continue; 1051 } 1052 1053 infos[count++] = info.info.node_info; 1054 } 1055 1056 *_count = count; 1057 return B_OK; 1058 } 1059 1060 1061 status_t 1062 NodeManager::GetDormantFlavorInfoFor(media_addon_id addOnID, int32 flavorID, 1063 dormant_flavor_info* flavorInfo) 1064 { 1065 BAutolock _(this); 1066 1067 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 1068 iterator != fDormantFlavors.end(); iterator++) { 1069 dormant_add_on_flavor_info& info = *iterator; 1070 1071 if (info.add_on_id == addOnID && info.flavor_id == flavorID 1072 && info.info_valid) { 1073 *flavorInfo = info.info; 1074 return B_OK; 1075 } 1076 } 1077 1078 return B_ERROR; 1079 } 1080 1081 1082 // #pragma mark - Misc. 1083 1084 1085 void 1086 NodeManager::CleanupTeam(team_id team) 1087 { 1088 BAutolock _(this); 1089 1090 fDefaultManager->CleanupTeam(team); 1091 1092 PRINT(1, "NodeManager::CleanupTeam: team %" B_PRId32 "\n", team); 1093 1094 // TODO: send notifications after removing nodes 1095 1096 // Cleanup node references 1097 1098 for (NodeMap::iterator iterator = fNodeMap.begin(); 1099 iterator != fNodeMap.end();) { 1100 registered_node& node = iterator->second; 1101 NodeMap::iterator remove = iterator++; 1102 1103 // If the gone team was the creator of some global dormant node 1104 // instance, we now invalidate that we may want to remove that 1105 // global node, but I'm not sure 1106 if (node.creator == team) { 1107 node.creator = -1; 1108 // fall through 1109 } 1110 1111 // If the team hosting this node is gone, remove node from database 1112 if (node.containing_team == team) { 1113 PRINT(1, "NodeManager::CleanupTeam: removing node id %" B_PRId32 1114 ", team %" B_PRId32 "\n", node.node_id, team); 1115 fNodeMap.erase(remove); 1116 continue; 1117 } 1118 1119 // Check the list of teams that have references to this node, and 1120 // remove the team 1121 TeamCountMap::iterator teamRef = node.team_ref_count.find(team); 1122 if (teamRef != node.team_ref_count.end()) { 1123 PRINT(1, "NodeManager::CleanupTeam: removing %" B_PRId32 " refs " 1124 "from node id %" B_PRId32 ", team %" B_PRId32 "\n", 1125 teamRef->second, node.node_id, team); 1126 node.ref_count -= teamRef->second; 1127 if (node.ref_count == 0) { 1128 PRINT(1, "NodeManager::CleanupTeam: removing node id %" 1129 B_PRId32 " that has no teams\n", node.node_id); 1130 1131 fNodeMap.erase(remove); 1132 } else 1133 node.team_ref_count.erase(teamRef); 1134 } 1135 } 1136 1137 // Cleanup add-on references 1138 1139 for (size_t index = 0; index < fDormantFlavors.size(); index++) { 1140 dormant_add_on_flavor_info& flavorInfo = fDormantFlavors[index]; 1141 1142 TeamCountMap::iterator instanceCount 1143 = flavorInfo.team_instances_count.find(team); 1144 if (instanceCount != flavorInfo.team_instances_count.end()) { 1145 PRINT(1, "NodeManager::CleanupTeam: removing %" B_PRId32 " " 1146 "instances from addon %" B_PRId32 ", flavor %" B_PRId32 "\n", 1147 instanceCount->second, flavorInfo.add_on_id, 1148 flavorInfo.flavor_id); 1149 1150 flavorInfo.instances_count -= instanceCount->second; 1151 if (flavorInfo.instances_count <= 0) 1152 fDormantFlavors.erase(fDormantFlavors.begin() + index--); 1153 else 1154 flavorInfo.team_instances_count.erase(team); 1155 } 1156 } 1157 } 1158 1159 1160 status_t 1161 NodeManager::LoadState() 1162 { 1163 BAutolock _(this); 1164 return fDefaultManager->LoadState(); 1165 } 1166 1167 status_t 1168 NodeManager::SaveState() 1169 { 1170 BAutolock _(this); 1171 return fDefaultManager->SaveState(this); 1172 } 1173 1174 1175 void 1176 NodeManager::Dump() 1177 { 1178 BAutolock _(this); 1179 1180 // for each addon-id, the add-on path map contains an entry_ref 1181 1182 printf("\nNodeManager: addon path map follows:\n"); 1183 1184 for (PathMap::iterator iterator = fPathMap.begin(); 1185 iterator != fPathMap.end(); iterator++) { 1186 BPath path(&iterator->second); 1187 printf(" addon-id %" B_PRId32 ", path \"%s\"\n", iterator->first, 1188 path.InitCheck() == B_OK ? path.Path() : "INVALID"); 1189 } 1190 1191 printf("NodeManager: list end\n\n"); 1192 1193 // for each node-id, the registered node map contians information about 1194 // source of the node, users, etc. 1195 1196 printf("NodeManager: registered nodes map follows:\n"); 1197 for (NodeMap::iterator iterator = fNodeMap.begin(); 1198 iterator != fNodeMap.end(); iterator++) { 1199 registered_node& node = iterator->second; 1200 1201 printf(" node-id %" B_PRId32 ", addon-id %" B_PRId32 ", addon-flavor-" 1202 "id %" B_PRId32 ", port %" B_PRId32 ", creator %" B_PRId32 ", " 1203 "team %" B_PRId32 ", kinds %#08" B_PRIx64 ", name \"%s\", " 1204 "ref_count %" B_PRId32 "\n", node.node_id, node.add_on_id, 1205 node.flavor_id, node.port, node.creator, node.containing_team, 1206 node.kinds, node.name, node.ref_count); 1207 1208 printf(" teams (refcount): "); 1209 for (TeamCountMap::iterator refsIterator = node.team_ref_count.begin(); 1210 refsIterator != node.team_ref_count.end(); refsIterator++) { 1211 printf("%" B_PRId32 " (%" B_PRId32 "), ", refsIterator->first, 1212 refsIterator->second); 1213 } 1214 printf("\n"); 1215 1216 for (InputList::iterator inIterator = node.input_list.begin(); 1217 inIterator != node.input_list.end(); inIterator++) { 1218 media_input& input = *inIterator; 1219 printf(" media_input: node-id %" B_PRId32 ", node-port %" 1220 B_PRId32 ", source-port %" B_PRId32 ", source-id %" B_PRId32 1221 ", dest-port %" B_PRId32 ", dest-id %" B_PRId32 ", name " 1222 "\"%s\"\n", input.node.node, input.node.port, input.source.port, 1223 input.source.id, input.destination.port, input.destination.id, 1224 input.name); 1225 } 1226 if (node.input_list.empty()) 1227 printf(" media_input: none\n"); 1228 1229 for (OutputList::iterator outIterator = node.output_list.begin(); 1230 outIterator != node.output_list.end(); outIterator++) { 1231 media_output& output = *outIterator; 1232 printf(" media_output: node-id %" B_PRId32 ", node-port %" 1233 B_PRId32 ", source-port %" B_PRId32 ", source-id %" B_PRId32 1234 ", dest-port %" B_PRId32 ", dest-id %" B_PRId32 ", name " 1235 "\"%s\"\n", output.node.node, output.node.port, 1236 output.source.port, output.source.id, output.destination.port, 1237 output.destination.id, output.name); 1238 } 1239 if (node.output_list.empty()) 1240 printf(" media_output: none\n"); 1241 } 1242 1243 printf("NodeManager: list end\n"); 1244 printf("\n"); 1245 1246 // Dormant add-on flavors 1247 1248 printf("NodeManager: dormant flavor list follows:\n"); 1249 1250 for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); 1251 iterator != fDormantFlavors.end(); iterator++) { 1252 dormant_add_on_flavor_info& flavorInfo = *iterator; 1253 1254 printf(" addon-id %" B_PRId32 ", flavor-id %" B_PRId32 ", max " 1255 "instances count %" B_PRId32 ", instances count %" B_PRId32 ", " 1256 "info valid %s\n", flavorInfo.add_on_id, flavorInfo.flavor_id, 1257 flavorInfo.max_instances_count, flavorInfo.instances_count, 1258 flavorInfo.info_valid ? "yes" : "no"); 1259 printf(" teams (instances): "); 1260 for (TeamCountMap::iterator countIterator 1261 = flavorInfo.team_instances_count.begin(); 1262 countIterator != flavorInfo.team_instances_count.end(); 1263 countIterator++) { 1264 printf("%" B_PRId32 " (%" B_PRId32 "), ", countIterator->first, 1265 countIterator->second); 1266 } 1267 printf("\n"); 1268 if (!flavorInfo.info_valid) 1269 continue; 1270 1271 printf(" addon-id %" B_PRId32 ", addon-flavor-id %" B_PRId32 ", " 1272 "addon-name \"%s\"\n", flavorInfo.info.node_info.addon, 1273 flavorInfo.info.node_info.flavor_id, 1274 flavorInfo.info.node_info.name); 1275 printf(" flavor-kinds %#08" B_PRIx64 ", flavor_flags %#08" B_PRIx32 1276 ", internal_id %" B_PRId32 ", possible_count %" B_PRId32 ", " 1277 "in_format_count %" B_PRId32 ", out_format_count %" B_PRId32 "\n", 1278 flavorInfo.info.kinds, flavorInfo.info.flavor_flags, 1279 flavorInfo.info.internal_id, flavorInfo.info.possible_count, 1280 flavorInfo.info.in_format_count, flavorInfo.info.out_format_count); 1281 printf(" flavor-name \"%s\"\n", flavorInfo.info.name); 1282 printf(" flavor-info \"%s\"\n", flavorInfo.info.info); 1283 } 1284 printf("NodeManager: list end\n"); 1285 1286 fDefaultManager->Dump(); 1287 } 1288 1289 1290 // #pragma mark - private methods 1291 1292 1293 status_t 1294 NodeManager::_AcquireNodeReference(media_node_id id, team_id team) 1295 { 1296 TRACE("NodeManager::_AcquireNodeReference enter: node %" B_PRId32 ", team " 1297 "%" B_PRId32 "\n", id, team); 1298 1299 BAutolock _(this); 1300 1301 NodeMap::iterator found = fNodeMap.find(id); 1302 if (found == fNodeMap.end()) { 1303 ERROR("NodeManager::_AcquireNodeReference: node %" B_PRId32 " not " 1304 "found\n", id); 1305 return B_ERROR; 1306 } 1307 1308 registered_node& node = found->second; 1309 1310 TeamCountMap::iterator teamRef = node.team_ref_count.find(team); 1311 if (teamRef == node.team_ref_count.end()) { 1312 // This is the team's first reference 1313 try { 1314 node.team_ref_count.insert(std::make_pair(team, 1)); 1315 } catch (std::bad_alloc& exception) { 1316 return B_NO_MEMORY; 1317 } 1318 } else { 1319 // Just increase its ref count 1320 teamRef->second++; 1321 } 1322 1323 node.ref_count++; 1324 1325 TRACE("NodeManager::_AcquireNodeReference leave: node %" B_PRId32 ", team " 1326 "%" B_PRId32 ", ref %" B_PRId32 ", team ref %" B_PRId32 "\n", id, team, 1327 node.ref_count, node.team_ref_count.find(team)->second); 1328 return B_OK; 1329 } 1330