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 #include <OS.h> 31 #include <Entry.h> 32 #include <Message.h> 33 #include <Locker.h> 34 #include <Autolock.h> 35 #include <Path.h> 36 #include <Messenger.h> 37 #include <MediaDefs.h> 38 #include <MediaAddOn.h> 39 #include "debug.h" 40 #include "NodeManager.h" 41 #include "DefaultManager.h" 42 #include "AppManager.h" 43 #include "MediaMisc.h" 44 45 extern AppManager *gAppManager; 46 47 const char *get_node_type(node_type t); 48 49 NodeManager::NodeManager() : 50 fNextAddOnID(1), 51 fNextNodeID(1), 52 fLocker(new BLocker("node manager locker")), 53 fDormantAddonFlavorList(new List<dormant_addon_flavor_info>), 54 fAddonPathMap(new Map<media_addon_id, entry_ref>), 55 fRegisteredNodeMap(new Map<media_node_id, registered_node>), 56 fDefaultManager(new DefaultManager) 57 { 58 } 59 60 61 NodeManager::~NodeManager() 62 { 63 delete fLocker; 64 delete fDormantAddonFlavorList; 65 delete fAddonPathMap; 66 delete fRegisteredNodeMap; 67 delete fDefaultManager; 68 } 69 70 /********************************************************************** 71 * Live node management 72 **********************************************************************/ 73 74 status_t 75 NodeManager::RegisterNode(media_node_id *nodeid, media_addon_id addon_id, int32 addon_flavor_id, const char *name, uint64 kinds, port_id port, team_id team) 76 { 77 BAutolock lock(fLocker); 78 bool b; 79 registered_node rn; 80 rn.nodeid = fNextNodeID; 81 rn.addon_id = addon_id; 82 rn.addon_flavor_id = addon_flavor_id; 83 strcpy(rn.name, name); 84 rn.kinds = kinds; 85 rn.port = port; 86 rn.team = team; 87 rn.creator = -1; // will be set later 88 rn.globalrefcount = 1; 89 rn.teamrefcount.Insert(team, 1); 90 91 b = fRegisteredNodeMap->Insert(fNextNodeID, rn); 92 ASSERT(b); 93 *nodeid = fNextNodeID; 94 fNextNodeID += 1; 95 TRACE("NodeManager::RegisterNode: node %ld, addon_id %ld, flavor_id %ld, name \"%s\", kinds %#Lx, port %ld, team %ld\n", *nodeid, addon_id, addon_flavor_id, name, kinds, port, team); 96 return B_OK; 97 } 98 99 100 status_t 101 NodeManager::UnregisterNode(media_addon_id *addonid, int32 *flavorid, media_node_id nodeid, team_id team) 102 { 103 BAutolock lock(fLocker); 104 bool b; 105 registered_node *rn; 106 TRACE("NodeManager::UnregisterNode enter: node %ld, team %ld\n", nodeid, team); 107 b = fRegisteredNodeMap->Get(nodeid, &rn); 108 if (!b) { 109 ERROR("NodeManager::UnregisterNode: couldn't find node %ld (team %ld)\n", nodeid, team); 110 return B_ERROR; 111 } 112 if (rn->team != team) { 113 ERROR("NodeManager::UnregisterNode: team %ld tried to unregister node %ld, but it was instantiated by team %ld\n", team, nodeid, rn->team); 114 return B_ERROR; 115 } 116 if (rn->globalrefcount != 1) { 117 ERROR("NodeManager::UnregisterNode: node %ld, team %ld has globalrefcount %ld (should be 1)\n", nodeid, team, rn->globalrefcount); 118 //return B_ERROR; 119 } 120 *addonid = rn->addon_id; 121 *flavorid = rn->addon_flavor_id; 122 b = fRegisteredNodeMap->Remove(nodeid); 123 ASSERT(b); 124 TRACE("NodeManager::UnregisterNode leave: node %ld, addon_id %ld, flavor_id %ld team %ld\n", nodeid, *addonid, *flavorid, team); 125 return B_OK; 126 } 127 128 129 status_t 130 NodeManager::IncrementGlobalRefCount(media_node_id nodeid, team_id team) 131 { 132 BAutolock lock(fLocker); 133 registered_node *rn; 134 bool b; 135 TRACE("NodeManager::IncrementGlobalRefCount enter: node %ld, team %ld\n", nodeid, team); 136 b = fRegisteredNodeMap->Get(nodeid, &rn); 137 if (!b) { 138 ERROR("NodeManager::IncrementGlobalRefCount: node %ld not found\n", nodeid); 139 return B_ERROR; 140 } 141 int32 *count; 142 int32 debug_count; 143 b = rn->teamrefcount.Get(team, &count); 144 if (b) { 145 *count += 1; 146 debug_count = *count; 147 } else { 148 b = rn->teamrefcount.Insert(team, 1); 149 ASSERT(b); 150 debug_count = 1; 151 } 152 rn->globalrefcount += 1; 153 TRACE("NodeManager::IncrementGlobalRefCount leave: node %ld, team %ld, count %ld, globalcount %ld\n", nodeid, team, debug_count, rn->globalrefcount); 154 return B_OK; 155 } 156 157 158 status_t 159 NodeManager::DecrementGlobalRefCount(media_node_id nodeid, team_id team) 160 { 161 BAutolock lock(fLocker); 162 registered_node *rn; 163 bool b; 164 TRACE("NodeManager::DecrementGlobalRefCount enter: node %ld, team %ld\n", nodeid, team); 165 b = fRegisteredNodeMap->Get(nodeid, &rn); 166 if (!b) { 167 ERROR("NodeManager::DecrementGlobalRefCount: node %ld not found\n", nodeid); 168 return B_ERROR; 169 } 170 int32 *count; 171 b = rn->teamrefcount.Get(team, &count); 172 if (!b) { 173 // Normally it is an error to release a node in another team. But we make one 174 // exception. If the node is global, and the creator team tries to release it, 175 // we will release it in the the media_addon_server. 176 team_id addon_server_team; 177 addon_server_team = gAppManager->AddonServerTeam(); 178 if (rn->creator == team && rn->teamrefcount.Get(addon_server_team, &count)) { 179 printf("!!! NodeManager::DecrementGlobalRefCount doing global release!\n"); 180 rn->creator = -1; //invalidate! 181 team = addon_server_team; //redirect! 182 // the count variable was already redirected in if() statement above. 183 } else { 184 ERROR("NodeManager::DecrementGlobalRefCount: node %ld has no team %ld references\n", nodeid, team); 185 return B_ERROR; 186 } 187 } 188 *count -= 1; 189 #if DEBUG >= 2 190 int32 debug_count = *count; 191 #endif 192 if (*count == 0) { 193 b = rn->teamrefcount.Remove(team); 194 ASSERT(b); 195 } 196 rn->globalrefcount -= 1; 197 198 if (rn->globalrefcount == 0) { 199 printf("NodeManager::DecrementGlobalRefCount: detected released node is now unused, node %ld\n", nodeid); 200 FinalReleaseNode(nodeid); 201 } 202 203 TRACE("NodeManager::DecrementGlobalRefCount leave: node %ld, team %ld, count %ld, globalcount %ld\n", nodeid, team, debug_count, rn->globalrefcount); 204 return B_OK; 205 } 206 207 status_t 208 NodeManager::SetNodeCreator(media_node_id nodeid, team_id creator) 209 { 210 BAutolock lock(fLocker); 211 registered_node *rn; 212 bool b; 213 214 TRACE("NodeManager::SetNodeCreator node %ld, creator %ld\n", nodeid, creator); 215 216 b = fRegisteredNodeMap->Get(nodeid, &rn); 217 if (!b) { 218 ERROR("NodeManager::SetNodeCreator: node %ld not found\n", nodeid); 219 return B_ERROR; 220 } 221 222 if (rn->creator != -1) { 223 ERROR("NodeManager::SetNodeCreator: node %ld is already assigned creator %ld\n", nodeid, rn->creator); 224 return B_ERROR; 225 } 226 227 rn->creator = creator; 228 return B_OK; 229 } 230 231 void 232 NodeManager::FinalReleaseNode(media_node_id nodeid) 233 { 234 BAutolock lock(fLocker); 235 registered_node *rn; 236 bool b; 237 status_t rv; 238 239 TRACE("NodeManager::FinalReleaseNode enter: node %ld\n", nodeid); 240 b = fRegisteredNodeMap->Get(nodeid, &rn); 241 if (!b) { 242 ERROR("NodeManager::FinalReleaseNode: node %ld not found\n", nodeid); 243 return; 244 } 245 246 node_final_release_command cmd; 247 rv = SendToPort(rn->port, NODE_FINAL_RELEASE, &cmd, sizeof(cmd)); 248 if (rv != B_OK) { 249 ERROR("NodeManager::FinalReleaseNode: can't send command to node %ld\n", nodeid); 250 return; 251 } 252 } 253 254 255 status_t 256 NodeManager::GetCloneForId(media_node *node, media_node_id nodeid, team_id team) 257 { 258 BAutolock lock(fLocker); 259 registered_node *rn; 260 bool b; 261 TRACE("NodeManager::GetCloneForId enter: node %ld team %ld\n", nodeid, team); 262 263 if (B_OK != IncrementGlobalRefCount(nodeid, team)) { 264 ERROR("NodeManager::GetCloneForId: couldn't increment ref count, node %ld team %ld\n", nodeid, team); 265 return B_ERROR; 266 } 267 268 b = fRegisteredNodeMap->Get(nodeid, &rn); 269 if (!b) { 270 ERROR("NodeManager::GetCloneForId: node %ld not found\n", nodeid); 271 DecrementGlobalRefCount(nodeid, team); 272 return B_ERROR; 273 } 274 275 node->node = rn->nodeid; 276 node->port = rn->port; 277 node->kind = rn->kinds; 278 279 TRACE("NodeManager::GetCloneForId leave: node %ld team %ld\n", nodeid, team); 280 return B_OK; 281 } 282 283 284 /* This function locates the default "node" for the requested "type" and returnes a clone. 285 * If the requested type is AUDIO_OUTPUT_EX, also "input_name" and "input_id" need to be set and returned, 286 * as this is required by BMediaRoster::GetAudioOutput(media_node *out_node, int32 *out_input_id, BString *out_input_name) 287 */ 288 status_t 289 NodeManager::GetClone(media_node *node, char *input_name, int32 *input_id, node_type type, team_id team) 290 { 291 BAutolock lock(fLocker); 292 status_t status; 293 media_node_id id; 294 295 TRACE("NodeManager::GetClone enter: team %ld, type %d (%s)\n", team, type, get_node_type(type)); 296 297 status = GetDefaultNode(&id, input_name, input_id, type); 298 if (status != B_OK) { 299 ERROR("NodeManager::GetClone: couldn't GetDefaultNode, team %ld, type %d (%s)\n", team, type, get_node_type(type)); 300 *node = media_node::null; 301 return status; 302 } 303 ASSERT(id > 0); 304 305 status = GetCloneForId(node, id, team); 306 if (status != B_OK) { 307 ERROR("NodeManager::GetClone: couldn't GetCloneForId, id %ld, team %ld, type %d (%s)\n", id, team, type, get_node_type(type)); 308 *node = media_node::null; 309 return status; 310 } 311 ASSERT(id == node->node); 312 313 TRACE("NodeManager::GetClone leave: node id %ld, node port %ld, node kind %#lx\n", node->node, node->port, node->kind); 314 315 return B_OK; 316 } 317 318 319 status_t 320 NodeManager::ReleaseNode(const media_node &node, team_id team) 321 { 322 BAutolock lock(fLocker); 323 TRACE("NodeManager::ReleaseNode enter: node %ld team %ld\n", node.node, team); 324 if (B_OK != DecrementGlobalRefCount(node.node, team)) { 325 ERROR("NodeManager::ReleaseNode: couldn't decrement node %ld team %ld ref count\n", node.node, team); 326 } 327 TRACE("NodeManager::ReleaseNode leave: node %ld team %ld\n", node.node, team); 328 return B_OK; 329 } 330 331 332 status_t 333 NodeManager::PublishInputs(const media_node &node, const media_input *inputs, int32 count) 334 { 335 BAutolock lock(fLocker); 336 registered_node *rn; 337 bool b; 338 b = fRegisteredNodeMap->Get(node.node, &rn); 339 if (!b) { 340 ERROR("NodeManager::PublishInputs: node %ld not found\n", node.node); 341 return B_ERROR; 342 } 343 rn->inputlist.MakeEmpty(); 344 for (int32 i = 0; i < count; i++) 345 rn->inputlist.Insert(inputs[i]); 346 return B_OK; 347 } 348 349 350 status_t 351 NodeManager::PublishOutputs(const media_node &node, const media_output *outputs, int32 count) 352 { 353 BAutolock lock(fLocker); 354 registered_node *rn; 355 bool b; 356 b = fRegisteredNodeMap->Get(node.node, &rn); 357 if (!b) { 358 ERROR("NodeManager::PublishOutputs: node %ld not found\n", node.node); 359 return B_ERROR; 360 } 361 rn->outputlist.MakeEmpty(); 362 for (int32 i = 0; i < count; i++) 363 rn->outputlist.Insert(outputs[i]); 364 return B_OK; 365 } 366 367 368 status_t 369 NodeManager::FindNodeId(media_node_id *nodeid, port_id port) 370 { 371 BAutolock lock(fLocker); 372 registered_node *rn; 373 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 374 if (rn->port == port) { 375 *nodeid = rn->nodeid; 376 TRACE("NodeManager::FindNodeId found port %ld, node %ld\n", port, *nodeid); 377 return B_OK; 378 } 379 media_output *output; 380 for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { 381 if (output->source.port == port) { 382 *nodeid = rn->nodeid; 383 TRACE("NodeManager::FindNodeId found output port %ld, node %ld\n", port, *nodeid); 384 return B_OK; 385 } 386 } 387 media_input *input; 388 for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { 389 if (input->destination.port == port) { 390 *nodeid = rn->nodeid; 391 TRACE("NodeManager::FindNodeId found input port %ld, node %ld\n", port, *nodeid); 392 return B_OK; 393 } 394 } 395 } 396 ERROR("NodeManager::FindNodeId failed, port %ld\n", port); 397 return B_ERROR; 398 } 399 400 status_t 401 NodeManager::GetDormantNodeInfo(dormant_node_info *node_info, const media_node &node) 402 { 403 BAutolock lock(fLocker); 404 // XXX not sure if this is correct 405 registered_node *rn; 406 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 407 if (rn->nodeid == node.node) { 408 if (rn->addon_id == -1 && node.node != NODE_SYSTEM_TIMESOURCE_ID) { // This function must return an error if the node is application owned 409 TRACE("NodeManager::GetDormantNodeInfo NODE IS APPLICATION OWNED! node %ld, addon_id %ld, addon_flavor_id %ld, name \"%s\"\n", node.node, rn->addon_id, rn->addon_flavor_id, rn->name); 410 return B_ERROR; 411 } 412 ASSERT(node.port == rn->port); 413 ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (rn->kinds & NODE_KIND_COMPARE_MASK)); 414 node_info->addon = rn->addon_id; 415 node_info->flavor_id = rn->addon_flavor_id; 416 strcpy(node_info->name, rn->name); 417 TRACE("NodeManager::GetDormantNodeInfo node %ld, addon_id %ld, addon_flavor_id %ld, name \"%s\"\n", node.node, rn->addon_id, rn->addon_flavor_id, rn->name); 418 return B_OK; 419 } 420 } 421 ERROR("NodeManager::GetDormantNodeInfo failed, node %ld\n", node.node); 422 return B_ERROR; 423 } 424 425 status_t 426 NodeManager::GetLiveNodeInfo(live_node_info *live_info, const media_node &node) 427 { 428 BAutolock lock(fLocker); 429 registered_node *rn; 430 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 431 if (rn->nodeid == node.node) { 432 ASSERT(node.port == rn->port); 433 ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (rn->kinds & NODE_KIND_COMPARE_MASK)); 434 live_info->node = node; 435 live_info->hint_point = BPoint(0, 0); 436 strcpy(live_info->name, rn->name); 437 TRACE("NodeManager::GetLiveNodeInfo node %ld, name = \"%s\"\n", node.node, rn->name); 438 return B_OK; 439 } 440 } 441 ERROR("NodeManager::GetLiveNodeInfo failed, node %ld\n", node.node); 442 return B_ERROR; 443 } 444 445 446 status_t 447 NodeManager::GetInstances(media_node_id *node_ids, int32* count, int32 maxcount, media_addon_id addon_id, int32 addon_flavor_id) 448 { 449 BAutolock lock(fLocker); 450 registered_node *rn; 451 *count = 0; 452 for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) { 453 if (rn->addon_id == addon_id && rn->addon_flavor_id == addon_flavor_id) { 454 node_ids[*count] = rn->nodeid; 455 *count += 1; 456 maxcount -= 1; 457 } 458 } 459 TRACE("NodeManager::GetInstances found %ld instances for addon_id %ld, addon_flavor_id %ld\n", *count, addon_id, addon_flavor_id); 460 return B_OK; 461 } 462 463 464 status_t 465 NodeManager::GetLiveNodes(Stack<live_node_info> *livenodes, int32 maxcount, const media_format *inputformat /* = NULL */, const media_format *outputformat /* = NULL */, const char* name /* = NULL */, uint64 require_kinds /* = 0 */) 466 { 467 BAutolock lock(fLocker); 468 registered_node *rn; 469 int namelen; 470 471 TRACE("NodeManager::GetLiveNodes: maxcount %ld, in-format %p, out-format %p, name %s, require_kinds 0x%Lx\n", 472 maxcount, inputformat, outputformat, (name ? name : "NULL"), require_kinds); 473 474 // determine the count of byte to compare when checking for a name with(out) wildcard 475 if (name) { 476 namelen = strlen(name); 477 if (namelen > 0 && name[namelen - 1] == '*') 478 namelen--; // compares without the '*' 479 } else { 480 namelen = 0; 481 } 482 483 for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) { 484 if ((rn->kinds & require_kinds) != require_kinds) 485 continue; 486 if (namelen) { 487 if (0 != strncmp(name, rn->name, namelen)) 488 continue; 489 } 490 if (inputformat) { 491 bool hasit = false; 492 media_input *input; 493 for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { 494 if (format_is_compatible(*inputformat, input->format)) { 495 hasit = true; 496 break; 497 } 498 } 499 if (!hasit) 500 continue; 501 } 502 if (outputformat) { 503 bool hasit = false; 504 media_output *output; 505 for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { 506 if (format_is_compatible(*outputformat, output->format)) { 507 hasit = true; 508 break; 509 } 510 } 511 if (!hasit) 512 continue; 513 } 514 515 live_node_info lni; 516 lni.node.node = rn->nodeid; 517 lni.node.port = rn->port; 518 lni.node.kind = rn->kinds; 519 lni.hint_point = BPoint(0, 0); 520 strcpy(lni.name, rn->name); 521 livenodes->Push(lni); 522 maxcount -= 1; 523 } 524 525 TRACE("NodeManager::GetLiveNodes found %ld\n", livenodes->CountItems()); 526 return B_OK; 527 } 528 529 /* Add media_node_id of all live nodes to the message 530 * int32 "media_node_id" (multiple items) 531 */ 532 status_t 533 NodeManager::GetLiveNodes(BMessage *msg) 534 { 535 BAutolock lock(fLocker); 536 registered_node *rn; 537 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 538 msg->AddInt32("media_node_id", rn->nodeid); 539 } 540 return B_OK; 541 } 542 543 /********************************************************************** 544 * Registration of BMediaAddOns 545 **********************************************************************/ 546 547 void 548 NodeManager::RegisterAddon(const entry_ref &ref, media_addon_id *newid) 549 { 550 BAutolock lock(fLocker); 551 media_addon_id id; 552 id = fNextAddOnID; 553 fNextAddOnID += 1; 554 555 printf("NodeManager::RegisterAddon: ref-name \"%s\", assigning id %ld\n", ref.name, id); 556 557 fAddonPathMap->Insert(id, ref); 558 *newid = id; 559 } 560 561 void 562 NodeManager::UnregisterAddon(media_addon_id id) 563 { 564 BAutolock lock(fLocker); 565 566 printf("NodeManager::UnregisterAddon: id %ld\n", id); 567 568 RemoveDormantFlavorInfo(id); 569 fAddonPathMap->Remove(id); 570 } 571 572 status_t 573 NodeManager::GetAddonRef(entry_ref *ref, media_addon_id id) 574 { 575 BAutolock lock(fLocker); 576 entry_ref *tempref; 577 578 if (fAddonPathMap->Get(id, &tempref)) { 579 *ref = *tempref; 580 return B_OK; 581 } 582 583 return B_ERROR; 584 } 585 586 /********************************************************************** 587 * Registration of node flavors, published by BMediaAddOns 588 **********************************************************************/ 589 590 // this function is only called (indirectly) by the media_addon_server 591 void 592 NodeManager::AddDormantFlavorInfo(const dormant_flavor_info &dfi) 593 { 594 BAutolock lock(fLocker); 595 596 printf("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n", dfi.node_info.addon, dfi.node_info.flavor_id, dfi.node_info.name, dfi.name, dfi.info); 597 598 // Try to find the addon-id/flavor-id in the list. 599 // If it already exists, update the Info, but don't 600 // change the GlobalInstancesCount 601 dormant_addon_flavor_info *dafi; 602 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 603 if (dafi->AddonID != dfi.node_info.addon || dafi->AddonFlavorID != dfi.node_info.flavor_id) 604 continue; 605 if (dafi->InfoValid) { 606 ERROR("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld does already exist\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id); 607 } 608 TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %ld, flavor-id %ld\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id); 609 dafi->MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff; 610 // do NOT modify dafi.GlobalInstancesCount 611 dafi->InfoValid = true; 612 dafi->Info = dfi; 613 return; 614 } 615 616 // Insert information into the list 617 { 618 dormant_addon_flavor_info dafi; 619 dafi.AddonID = dfi.node_info.addon; 620 dafi.AddonFlavorID = dfi.node_info.flavor_id; 621 dafi.MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff; 622 dafi.GlobalInstancesCount = 0; 623 dafi.InfoValid = true; 624 dafi.Info = dfi; 625 fDormantAddonFlavorList->Insert(dafi); 626 } 627 } 628 629 // this function is only called (indirectly) by the media_addon_server 630 void 631 NodeManager::InvalidateDormantFlavorInfo(media_addon_id id) 632 { 633 BAutolock lock(fLocker); 634 dormant_addon_flavor_info *dafi; 635 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 636 if (dafi->AddonID == id && dafi->InfoValid == true) { 637 printf("NodeManager::InvalidateDormantFlavorInfo, addon-id %ld, flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name, dafi->Info.name, dafi->Info.info); 638 dormant_flavor_info dfi_null; 639 dafi->Info = dfi_null; 640 dafi->InfoValid = false; 641 } 642 } 643 } 644 645 // this function is only called by clean up after gone add-ons 646 void 647 NodeManager::RemoveDormantFlavorInfo(media_addon_id id) 648 { 649 BAutolock lock(fLocker); 650 dormant_addon_flavor_info *dafi; 651 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 652 if (dafi->AddonID == id) { 653 printf("NodeManager::RemoveDormantFlavorInfo, addon-id %ld, flavor-id %ld, name \"%s\", flavor-name \"%s\", flavor-info \"%s\"\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name, dafi->Info.name, dafi->Info.info); 654 fDormantAddonFlavorList->RemoveCurrent(); 655 } 656 } 657 } 658 659 status_t 660 NodeManager::IncrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team) 661 { 662 BAutolock lock(fLocker); 663 664 dormant_addon_flavor_info *dafi; 665 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 666 if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid) 667 continue; 668 669 if (dafi->GlobalInstancesCount >= dafi->MaxInstancesCount) { 670 ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld maximum (or more) instances already exist\n", addonid, flavorid); 671 return B_ERROR; // maximum (or more) instances already exist 672 } 673 674 bool b; 675 int32 *count; 676 b = dafi->TeamInstancesCount.Get(team, &count); 677 if (b) { 678 *count += 1; 679 } else { 680 b = dafi->TeamInstancesCount.Insert(team, 1); 681 ASSERT(b); 682 } 683 dafi->GlobalInstancesCount += 1; 684 return B_OK; 685 } 686 ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid); 687 return B_ERROR; 688 } 689 690 status_t 691 NodeManager::DecrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team) 692 { 693 BAutolock lock(fLocker); 694 695 dormant_addon_flavor_info *dafi; 696 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 697 if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid) 698 continue; 699 700 bool b; 701 int32 *count; 702 b = dafi->TeamInstancesCount.Get(team, &count); 703 if (!b) { 704 ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld team %ld has no references\n", addonid, flavorid, team); 705 return B_ERROR; 706 } 707 *count -= 1; 708 if (*count == 0) { 709 b = dafi->TeamInstancesCount.Remove(team); 710 ASSERT(b); 711 } 712 if (dafi->GlobalInstancesCount > 0) // avoid underflow 713 dafi->GlobalInstancesCount -= 1; 714 return B_OK; 715 } 716 ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid); 717 return B_ERROR; 718 } 719 720 // this function is called when the media_addon_server has crashed 721 void 722 NodeManager::CleanupDormantFlavorInfos() 723 { 724 BAutolock lock(fLocker); 725 printf("NodeManager::CleanupDormantFlavorInfos\n"); 726 fDormantAddonFlavorList->MakeEmpty(); 727 printf("NodeManager::CleanupDormantFlavorInfos done\n"); 728 // XXX FlavorsChanged(media_addon_id addonid, int32 newcount, int32 gonecount) 729 } 730 731 status_t 732 NodeManager::GetDormantNodes(dormant_node_info * out_info, 733 int32 * io_count, 734 const media_format * has_input /* = NULL */, 735 const media_format * has_output /* = NULL */, 736 const char * name /* = NULL */, 737 uint64 require_kinds /* = NULL */, 738 uint64 deny_kinds /* = NULL */) 739 { 740 BAutolock lock(fLocker); 741 dormant_addon_flavor_info *dafi; 742 int32 maxcount; 743 int namelen; 744 745 // determine the count of byte to compare when checking for a name with(out) wildcard 746 if (name) { 747 namelen = strlen(name); 748 if (namelen > 0 && name[namelen - 1] == '*') 749 namelen--; // compares without the '*' 750 } else { 751 namelen = 0; 752 } 753 754 maxcount = *io_count; 755 *io_count = 0; 756 for (fDormantAddonFlavorList->Rewind(); (*io_count < maxcount) && fDormantAddonFlavorList->GetNext(&dafi); ) { 757 if (!dafi->InfoValid) 758 continue; 759 760 dormant_flavor_info *dfi; 761 dfi = &dafi->Info; 762 763 if ((dfi->kinds & require_kinds) != require_kinds) 764 continue; 765 if ((dfi->kinds & deny_kinds) != 0) 766 continue; 767 if (namelen) { 768 if (0 != strncmp(name, dfi->name, namelen)) 769 continue; 770 } 771 if (has_input) { 772 bool hasit = false; 773 for (int32 i = 0; i < dfi->in_format_count; i++) 774 if (format_is_compatible(*has_input, dfi->in_formats[i])) { 775 hasit = true; 776 break; 777 } 778 if (!hasit) 779 continue; 780 } 781 if (has_output) { 782 bool hasit = false; 783 for (int32 i = 0; i < dfi->out_format_count; i++) 784 if (format_is_compatible(*has_output, dfi->out_formats[i])) { 785 hasit = true; 786 break; 787 } 788 if (!hasit) 789 continue; 790 } 791 792 out_info[*io_count] = dfi->node_info; 793 *io_count += 1; 794 } 795 796 return B_OK; 797 } 798 799 status_t 800 NodeManager::GetDormantFlavorInfoFor(media_addon_id addon, 801 int32 flavor_id, 802 dormant_flavor_info *outFlavor) 803 { 804 BAutolock lock(fLocker); 805 dormant_addon_flavor_info *dafi; 806 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 807 if (dafi->AddonID == addon && dafi->AddonFlavorID == flavor_id && dafi->InfoValid == true) { 808 *outFlavor = dafi->Info; 809 return B_OK; 810 } 811 } 812 return B_ERROR; 813 } 814 815 /********************************************************************** 816 * Default node management 817 **********************************************************************/ 818 819 status_t 820 NodeManager::SetDefaultNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input) 821 { 822 BAutolock lock(fLocker); 823 status_t err = B_BAD_VALUE; 824 if (node) 825 err = fDefaultManager->Set(node->node, NULL, 0, type); 826 else if (input) 827 err = fDefaultManager->Set(input->node.node, input->name, input->destination.id, type); 828 else if (info) { 829 media_node_id node_id; 830 int32 count = 1; 831 if ((err=GetInstances(&node_id, &count, count, info->addon, info->flavor_id))!=B_OK) 832 return err; 833 err = fDefaultManager->Set(node_id, NULL, 0, type); 834 } 835 if(err==B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) { 836 fDefaultManager->SaveState(this); 837 Dump(); 838 } 839 return err; 840 } 841 842 status_t 843 NodeManager::GetDefaultNode(media_node_id *nodeid, char *input_name, int32 *input_id, node_type type) 844 { 845 BAutolock lock(fLocker); 846 return fDefaultManager->Get(nodeid, input_name, input_id, type); 847 } 848 849 status_t 850 NodeManager::RescanDefaultNodes() 851 { 852 BAutolock lock(fLocker); 853 return fDefaultManager->Rescan(); 854 } 855 856 /********************************************************************** 857 * Cleanup of dead teams 858 **********************************************************************/ 859 860 void 861 NodeManager::CleanupTeam(team_id team) 862 { 863 BAutolock lock(fLocker); 864 865 fDefaultManager->CleanupTeam(team); 866 867 PRINT(1, "NodeManager::CleanupTeam: team %ld\n", team); 868 869 // XXX send notifications after removing nodes 870 871 // Cleanup node references 872 873 registered_node *rn; 874 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 875 // if the gone team was the creator of some global dormant node instance, we now invalidate that 876 // we may want to remove that global node, but I'm not sure 877 if (rn->creator == team) { 878 rn->creator = -1; 879 // fall through 880 } 881 // if the team hosting this node is gone, remove node from database 882 if (rn->team == team) { 883 PRINT(1, "NodeManager::CleanupTeam: removing node id %ld, team %ld\n", rn->nodeid, team); 884 fRegisteredNodeMap->RemoveCurrent(); 885 continue; 886 } 887 // check the list of teams that have references to this node, and remove the team 888 team_id *pteam; 889 int32 *prefcount; 890 for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&prefcount); ) { 891 rn->teamrefcount.GetCurrentKey(&pteam); 892 if (*pteam == team) { 893 PRINT(1, "NodeManager::CleanupTeam: removing %ld refs from node id %ld, team %ld\n", *prefcount, rn->nodeid, team); 894 rn->teamrefcount.RemoveCurrent(); 895 break; 896 } 897 } 898 // if the team refcount is now empty, also remove the node 899 if (rn->teamrefcount.IsEmpty()) { 900 PRINT(1, "NodeManager::CleanupTeam: removing node id %ld that has no teams\n", rn->nodeid); 901 fRegisteredNodeMap->RemoveCurrent(); 902 } 903 } 904 905 // Cleanup addon references 906 dormant_addon_flavor_info *dafi; 907 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 908 bool b; 909 int32 *count; 910 b = dafi->TeamInstancesCount.Get(team, &count); 911 if (b) { 912 PRINT(1, "NodeManager::CleanupTeam: removing %ld instances from addon %ld, flavor %ld\n", *count, dafi->AddonID, dafi->AddonFlavorID); 913 dafi->GlobalInstancesCount -= *count; 914 if (dafi->GlobalInstancesCount < 0) // avoid underflow 915 dafi->GlobalInstancesCount = 0; 916 b = dafi->TeamInstancesCount.Remove(team); 917 ASSERT(b); 918 } 919 } 920 } 921 922 /********************************************************************** 923 * State saving/loading 924 **********************************************************************/ 925 926 status_t 927 NodeManager::LoadState() 928 { 929 BAutolock lock(fLocker); 930 return fDefaultManager->LoadState(); 931 } 932 933 status_t 934 NodeManager::SaveState() 935 { 936 BAutolock lock(fLocker); 937 return fDefaultManager->SaveState(this); 938 } 939 940 /********************************************************************** 941 * Debugging 942 **********************************************************************/ 943 944 void 945 NodeManager::Dump() 946 { 947 BAutolock lock(fLocker); 948 printf("\n"); 949 950 /* for each addon-id, the addon path map contains an entry_ref 951 */ 952 printf("NodeManager: addon path map follows:\n"); 953 entry_ref *ref; 954 media_addon_id *id; 955 for (fAddonPathMap->Rewind(); fAddonPathMap->GetNext(&ref); ) { 956 fAddonPathMap->GetCurrentKey(&id); 957 BPath path(ref); 958 printf(" addon-id %ld, ref-name \"%s\", path \"%s\"\n", *id, ref->name, (path.InitCheck() == B_OK) ? path.Path() : "INVALID"); 959 } 960 printf("NodeManager: list end\n"); 961 printf("\n"); 962 963 /* for each node-id, the registered node map contians information about source of the node, users, etc. 964 */ 965 printf("NodeManager: registered nodes map follows:\n"); 966 registered_node *rn; 967 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 968 printf(" node-id %ld, addon-id %ld, addon-flavor-id %ld, port %ld, creator %ld, team %ld, kinds %#08Lx, name \"%s\"\n", 969 rn->nodeid, rn->addon_id, rn->addon_flavor_id, rn->port, rn->creator, rn->team, rn->kinds, rn->name); 970 printf(" teams (refcount): "); 971 team_id *team; 972 int32 *refcount; 973 for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&refcount); ) { 974 rn->teamrefcount.GetCurrentKey(&team); 975 printf("%ld (%ld), ", *team, *refcount); 976 } 977 printf("\n"); 978 media_input *input; 979 for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { 980 printf(" media_input: node-id %ld, node-port %ld, source-port %ld, source-id %ld, dest-port %ld, dest-id %ld, name \"%s\"\n", 981 input->node.node, input->node.port, input->source.port, input->source.id, input->destination.port, input->destination.id, input->name); 982 } 983 if (rn->inputlist.IsEmpty()) 984 printf(" media_input: none\n"); 985 media_output *output; 986 for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { 987 printf(" media_output: node-id %ld, node-port %ld, source-port %ld, source-id %ld, dest-port %ld, dest-id %ld, name \"%s\"\n", 988 output->node.node, output->node.port, output->source.port, output->source.id, output->destination.port, output->destination.id, output->name); 989 } 990 if (rn->outputlist.IsEmpty()) 991 printf(" media_output: none\n"); 992 } 993 printf("NodeManager: list end\n"); 994 printf("\n"); 995 996 /* 997 */ 998 printf("NodeManager: dormant flavor list follows:\n"); 999 dormant_addon_flavor_info *dafi; 1000 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 1001 printf(" AddonID %ld, AddonFlavorID %ld, MaxInstancesCount %ld, GlobalInstancesCount %ld, InfoValid %s\n", 1002 dafi->AddonID, dafi->AddonFlavorID, dafi->MaxInstancesCount, dafi->GlobalInstancesCount, dafi->InfoValid ? "yes" : "no"); 1003 if (!dafi->InfoValid) 1004 continue; 1005 printf(" addon-id %ld, addon-flavor-id %ld, addon-name \"%s\"\n", 1006 dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name); 1007 printf(" flavor-kinds %#08Lx, flavor_flags %#08lx, internal_id %ld, possible_count %ld, in_format_count %ld, out_format_count %ld\n", 1008 dafi->Info.kinds, dafi->Info.flavor_flags, dafi->Info.internal_id, dafi->Info.possible_count, dafi->Info.in_format_count, dafi->Info.out_format_count); 1009 printf(" flavor-name \"%s\"\n", dafi->Info.name); 1010 printf(" flavor-info \"%s\"\n", dafi->Info.info); 1011 } 1012 printf("NodeManager: list end\n"); 1013 fDefaultManager->Dump(); 1014 } 1015 1016 const char * 1017 get_node_type(node_type t) 1018 { 1019 switch (t) { 1020 #define CASE(c) case c: return #c; 1021 CASE(VIDEO_INPUT) 1022 CASE(AUDIO_INPUT) 1023 CASE(VIDEO_OUTPUT) 1024 CASE(AUDIO_MIXER) 1025 CASE(AUDIO_OUTPUT) 1026 CASE(AUDIO_OUTPUT_EX) 1027 CASE(TIME_SOURCE) 1028 CASE(SYSTEM_TIME_SOURCE) 1029 default: return "unknown"; 1030 } 1031 }; 1032 1033