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 finde 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->AddonServer(); 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 (name[namelen] == '*') 478 namelen--; // compares without the '*' 479 else 480 namelen++; // also compares the terminating NULL 481 } else 482 namelen = 0; 483 484 for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) { 485 if ((rn->kinds & require_kinds) != require_kinds) 486 continue; 487 if (namelen) { 488 if (0 != memcmp(name, rn->name, namelen)) 489 continue; 490 } 491 if (inputformat) { 492 bool hasit = false; 493 media_input *input; 494 for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { 495 if (format_is_compatible(*inputformat, input->format)) { 496 hasit = true; 497 break; 498 } 499 } 500 if (!hasit) 501 continue; 502 } 503 if (outputformat) { 504 bool hasit = false; 505 media_output *output; 506 for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { 507 if (format_is_compatible(*outputformat, output->format)) { 508 hasit = true; 509 break; 510 } 511 } 512 if (!hasit) 513 continue; 514 } 515 516 live_node_info lni; 517 lni.node.node = rn->nodeid; 518 lni.node.port = rn->port; 519 lni.node.kind = rn->kinds; 520 lni.hint_point = BPoint(0, 0); 521 strcpy(lni.name, rn->name); 522 livenodes->Push(lni); 523 maxcount -= 1; 524 } 525 526 TRACE("NodeManager::GetLiveNodes found %ld\n", livenodes->CountItems()); 527 return B_OK; 528 } 529 530 /* Add media_node_id of all live nodes to the message 531 * int32 "media_node_id" (multiple items) 532 */ 533 status_t 534 NodeManager::GetLiveNodes(BMessage *msg) 535 { 536 BAutolock lock(fLocker); 537 registered_node *rn; 538 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 539 msg->AddInt32("media_node_id", rn->nodeid); 540 } 541 return B_OK; 542 } 543 544 /********************************************************************** 545 * Registration of BMediaAddOns 546 **********************************************************************/ 547 548 void 549 NodeManager::RegisterAddon(const entry_ref &ref, media_addon_id *newid) 550 { 551 BAutolock lock(fLocker); 552 media_addon_id id; 553 id = fNextAddOnID; 554 fNextAddOnID += 1; 555 556 printf("NodeManager::RegisterAddon: ref-name \"%s\", assigning id %ld\n", ref.name, id); 557 558 fAddonPathMap->Insert(id, ref); 559 *newid = id; 560 } 561 562 void 563 NodeManager::UnregisterAddon(media_addon_id id) 564 { 565 BAutolock lock(fLocker); 566 567 printf("NodeManager::UnregisterAddon: id %ld\n", id); 568 569 RemoveDormantFlavorInfo(id); 570 fAddonPathMap->Remove(id); 571 } 572 573 status_t 574 NodeManager::GetAddonRef(entry_ref *ref, media_addon_id id) 575 { 576 BAutolock lock(fLocker); 577 entry_ref *tempref; 578 579 if (fAddonPathMap->Get(id, &tempref)) { 580 *ref = *tempref; 581 return B_OK; 582 } 583 584 return B_ERROR; 585 } 586 587 /********************************************************************** 588 * Registration of node flavors, published by BMediaAddOns 589 **********************************************************************/ 590 591 // this function is only called (indirectly) by the media_addon_server 592 void 593 NodeManager::AddDormantFlavorInfo(const dormant_flavor_info &dfi) 594 { 595 BAutolock lock(fLocker); 596 597 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); 598 599 // Try to find the addon-id/flavor-id in the list. 600 // If it already exists, update the Info, but don't 601 // change the GlobalInstancesCount 602 dormant_addon_flavor_info *dafi; 603 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 604 if (dafi->AddonID != dfi.node_info.addon || dafi->AddonFlavorID != dfi.node_info.flavor_id) 605 continue; 606 if (dafi->InfoValid) { 607 ERROR("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld does already exist\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id); 608 } 609 TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %ld, flavor-id %ld\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id); 610 dafi->MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff; 611 // do NOT modify dafi.GlobalInstancesCount 612 dafi->InfoValid = true; 613 dafi->Info = dfi; 614 return; 615 } 616 617 // Insert information into the list 618 { 619 dormant_addon_flavor_info dafi; 620 dafi.AddonID = dfi.node_info.addon; 621 dafi.AddonFlavorID = dfi.node_info.flavor_id; 622 dafi.MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff; 623 dafi.GlobalInstancesCount = 0; 624 dafi.InfoValid = true; 625 dafi.Info = dfi; 626 fDormantAddonFlavorList->Insert(dafi); 627 } 628 } 629 630 // this function is only called (indirectly) by the media_addon_server 631 void 632 NodeManager::InvalidateDormantFlavorInfo(media_addon_id id) 633 { 634 BAutolock lock(fLocker); 635 dormant_addon_flavor_info *dafi; 636 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 637 if (dafi->AddonID == id && dafi->InfoValid == true) { 638 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); 639 dormant_flavor_info dfi_null; 640 dafi->Info = dfi_null; 641 dafi->InfoValid = false; 642 } 643 } 644 } 645 646 // this function is only called by clean up after gone add-ons 647 void 648 NodeManager::RemoveDormantFlavorInfo(media_addon_id id) 649 { 650 BAutolock lock(fLocker); 651 dormant_addon_flavor_info *dafi; 652 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 653 if (dafi->AddonID == id) { 654 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); 655 fDormantAddonFlavorList->RemoveCurrent(); 656 } 657 } 658 } 659 660 status_t 661 NodeManager::IncrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team) 662 { 663 BAutolock lock(fLocker); 664 665 dormant_addon_flavor_info *dafi; 666 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 667 if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid) 668 continue; 669 670 if (dafi->GlobalInstancesCount >= dafi->MaxInstancesCount) { 671 ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld maximum (or more) instances already exist\n", addonid, flavorid); 672 return B_ERROR; // maximum (or more) instances already exist 673 } 674 675 bool b; 676 int32 *count; 677 b = dafi->TeamInstancesCount.Get(team, &count); 678 if (b) { 679 *count += 1; 680 } else { 681 b = dafi->TeamInstancesCount.Insert(team, 1); 682 ASSERT(b); 683 } 684 dafi->GlobalInstancesCount += 1; 685 return B_OK; 686 } 687 ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid); 688 return B_ERROR; 689 } 690 691 status_t 692 NodeManager::DecrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team) 693 { 694 BAutolock lock(fLocker); 695 696 dormant_addon_flavor_info *dafi; 697 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 698 if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid) 699 continue; 700 701 bool b; 702 int32 *count; 703 b = dafi->TeamInstancesCount.Get(team, &count); 704 if (!b) { 705 ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld team %ld has no references\n", addonid, flavorid, team); 706 return B_ERROR; 707 } 708 *count -= 1; 709 if (*count == 0) { 710 b = dafi->TeamInstancesCount.Remove(team); 711 ASSERT(b); 712 } 713 if (dafi->GlobalInstancesCount > 0) // avoid underflow 714 dafi->GlobalInstancesCount -= 1; 715 return B_OK; 716 } 717 ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid); 718 return B_ERROR; 719 } 720 721 // this function is called when the media_addon_server has crashed 722 void 723 NodeManager::CleanupDormantFlavorInfos() 724 { 725 BAutolock lock(fLocker); 726 printf("NodeManager::CleanupDormantFlavorInfos\n"); 727 fDormantAddonFlavorList->MakeEmpty(); 728 printf("NodeManager::CleanupDormantFlavorInfos done\n"); 729 // XXX FlavorsChanged(media_addon_id addonid, int32 newcount, int32 gonecount) 730 } 731 732 status_t 733 NodeManager::GetDormantNodes(dormant_node_info * out_info, 734 int32 * io_count, 735 const media_format * has_input /* = NULL */, 736 const media_format * has_output /* = NULL */, 737 const char * name /* = NULL */, 738 uint64 require_kinds /* = NULL */, 739 uint64 deny_kinds /* = NULL */) 740 { 741 BAutolock lock(fLocker); 742 dormant_addon_flavor_info *dafi; 743 int32 maxcount; 744 int namelen; 745 746 // determine the count of byte to compare when checking for a name with(out) wildcard 747 if (name) { 748 namelen = strlen(name); 749 if (name[namelen] == '*') 750 namelen--; // compares without the '*' 751 else 752 namelen++; // also compares the terminating NULL 753 } else 754 namelen = 0; 755 756 maxcount = *io_count; 757 *io_count = 0; 758 for (fDormantAddonFlavorList->Rewind(); (*io_count < maxcount) && fDormantAddonFlavorList->GetNext(&dafi); ) { 759 if (!dafi->InfoValid) 760 continue; 761 762 dormant_flavor_info *dfi; 763 dfi = &dafi->Info; 764 765 if ((dfi->kinds & require_kinds) != require_kinds) 766 continue; 767 if ((dfi->kinds & deny_kinds) != 0) 768 continue; 769 if (namelen) { 770 if (0 != memcmp(name, dfi->name, namelen)) 771 continue; 772 } 773 if (has_input) { 774 bool hasit = false; 775 for (int32 i = 0; i < dfi->in_format_count; i++) 776 if (format_is_compatible(*has_input, dfi->in_formats[i])) { 777 hasit = true; 778 break; 779 } 780 if (!hasit) 781 continue; 782 } 783 if (has_output) { 784 bool hasit = false; 785 for (int32 i = 0; i < dfi->out_format_count; i++) 786 if (format_is_compatible(*has_output, dfi->out_formats[i])) { 787 hasit = true; 788 break; 789 } 790 if (!hasit) 791 continue; 792 } 793 794 out_info[*io_count] = dfi->node_info; 795 *io_count += 1; 796 } 797 798 return B_OK; 799 } 800 801 status_t 802 NodeManager::GetDormantFlavorInfoFor(media_addon_id addon, 803 int32 flavor_id, 804 dormant_flavor_info *outFlavor) 805 { 806 BAutolock lock(fLocker); 807 dormant_addon_flavor_info *dafi; 808 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 809 if (dafi->AddonID == addon && dafi->AddonFlavorID == flavor_id && dafi->InfoValid == true) { 810 *outFlavor = dafi->Info; 811 return B_OK; 812 } 813 } 814 return B_ERROR; 815 } 816 817 /********************************************************************** 818 * Default node management 819 **********************************************************************/ 820 821 status_t 822 NodeManager::SetDefaultNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input) 823 { 824 BAutolock lock(fLocker); 825 status_t err = B_BAD_VALUE; 826 if (node) 827 err = fDefaultManager->Set(node->node, NULL, 0, type); 828 else if (input) 829 err = fDefaultManager->Set(input->node.node, input->name, input->destination.id, type); 830 else if (info) { 831 media_node_id node_id; 832 int32 count = 1; 833 if ((err=GetInstances(&node_id, &count, count, info->addon, info->flavor_id))!=B_OK) 834 return err; 835 err = fDefaultManager->Set(node_id, NULL, 0, type); 836 } 837 if(err==B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) { 838 fDefaultManager->SaveState(this); 839 Dump(); 840 } 841 return err; 842 } 843 844 status_t 845 NodeManager::GetDefaultNode(media_node_id *nodeid, char *input_name, int32 *input_id, node_type type) 846 { 847 BAutolock lock(fLocker); 848 return fDefaultManager->Get(nodeid, input_name, input_id, type); 849 } 850 851 status_t 852 NodeManager::RescanDefaultNodes() 853 { 854 BAutolock lock(fLocker); 855 return fDefaultManager->Rescan(); 856 } 857 858 /********************************************************************** 859 * Cleanup of dead teams 860 **********************************************************************/ 861 862 void 863 NodeManager::CleanupTeam(team_id team) 864 { 865 BAutolock lock(fLocker); 866 867 fDefaultManager->CleanupTeam(team); 868 869 PRINT(1, "NodeManager::CleanupTeam: team %ld\n", team); 870 871 // XXX send notifications after removing nodes 872 873 // Cleanup node references 874 875 registered_node *rn; 876 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 877 // if the gone team was the creator of some global dormant node instance, we now invalidate that 878 // we may want to remove that global node, but I'm not sure 879 if (rn->creator == team) { 880 rn->creator = -1; 881 // fall through 882 } 883 // if the team hosting this node is gone, remove node from database 884 if (rn->team == team) { 885 PRINT(1, "NodeManager::CleanupTeam: removing node id %ld, team %ld\n", rn->nodeid, team); 886 fRegisteredNodeMap->RemoveCurrent(); 887 continue; 888 } 889 // check the list of teams that have references to this node, and remove the team 890 team_id *pteam; 891 int32 *prefcount; 892 for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&prefcount); ) { 893 rn->teamrefcount.GetCurrentKey(&pteam); 894 if (*pteam == team) { 895 PRINT(1, "NodeManager::CleanupTeam: removing %ld refs from node id %ld, team %ld\n", *prefcount, rn->nodeid, team); 896 rn->teamrefcount.RemoveCurrent(); 897 break; 898 } 899 } 900 // if the team refcount is now empty, also remove the node 901 if (rn->teamrefcount.IsEmpty()) { 902 PRINT(1, "NodeManager::CleanupTeam: removing node id %ld that has no teams\n", rn->nodeid); 903 fRegisteredNodeMap->RemoveCurrent(); 904 } 905 } 906 907 // Cleanup addon references 908 dormant_addon_flavor_info *dafi; 909 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 910 bool b; 911 int32 *count; 912 b = dafi->TeamInstancesCount.Get(team, &count); 913 if (b) { 914 PRINT(1, "NodeManager::CleanupTeam: removing %ld instances from addon %ld, flavor %ld\n", *count, dafi->AddonID, dafi->AddonFlavorID); 915 dafi->GlobalInstancesCount -= *count; 916 if (dafi->GlobalInstancesCount < 0) // avoid underflow 917 dafi->GlobalInstancesCount = 0; 918 b = dafi->TeamInstancesCount.Remove(team); 919 ASSERT(b); 920 } 921 } 922 } 923 924 /********************************************************************** 925 * State saving/loading 926 **********************************************************************/ 927 928 status_t 929 NodeManager::LoadState() 930 { 931 BAutolock lock(fLocker); 932 return fDefaultManager->LoadState(); 933 } 934 935 status_t 936 NodeManager::SaveState() 937 { 938 BAutolock lock(fLocker); 939 return fDefaultManager->SaveState(this); 940 } 941 942 /********************************************************************** 943 * Debugging 944 **********************************************************************/ 945 946 void 947 NodeManager::Dump() 948 { 949 BAutolock lock(fLocker); 950 printf("\n"); 951 952 /* for each addon-id, the addon path map contians an entry_ref 953 */ 954 printf("NodeManager: addon path map follows:\n"); 955 entry_ref *ref; 956 media_addon_id *id; 957 for (fAddonPathMap->Rewind(); fAddonPathMap->GetNext(&ref); ) { 958 fAddonPathMap->GetCurrentKey(&id); 959 BPath path(ref); 960 printf(" addon-id %ld, ref-name \"%s\", path \"%s\"\n", *id, ref->name, (path.InitCheck() == B_OK) ? path.Path() : "INVALID"); 961 } 962 printf("NodeManager: list end\n"); 963 printf("\n"); 964 965 /* for each node-id, the registered node map contians information about source of the node, users, etc. 966 */ 967 printf("NodeManager: registered nodes map follows:\n"); 968 registered_node *rn; 969 for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { 970 printf(" node-id %ld, addon-id %ld, addon-flavor-id %ld, port %ld, creator %ld, team %ld, kinds %#08Lx, name \"%s\"\n", 971 rn->nodeid, rn->addon_id, rn->addon_flavor_id, rn->port, rn->creator, rn->team, rn->kinds, rn->name); 972 printf(" teams (refcount): "); 973 team_id *team; 974 int32 *refcount; 975 for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&refcount); ) { 976 rn->teamrefcount.GetCurrentKey(&team); 977 printf("%ld (%ld), ", *team, *refcount); 978 } 979 printf("\n"); 980 media_input *input; 981 for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { 982 printf(" media_input: node-id %ld, node-port %ld, source-port %ld, source-id %ld, dest-port %ld, dest-id %ld, name \"%s\"\n", 983 input->node.node, input->node.port, input->source.port, input->source.id, input->destination.port, input->destination.id, input->name); 984 } 985 if (rn->inputlist.IsEmpty()) 986 printf(" media_input: none\n"); 987 media_output *output; 988 for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { 989 printf(" media_output: node-id %ld, node-port %ld, source-port %ld, source-id %ld, dest-port %ld, dest-id %ld, name \"%s\"\n", 990 output->node.node, output->node.port, output->source.port, output->source.id, output->destination.port, output->destination.id, output->name); 991 } 992 if (rn->outputlist.IsEmpty()) 993 printf(" media_output: none\n"); 994 } 995 printf("NodeManager: list end\n"); 996 printf("\n"); 997 998 /* 999 */ 1000 printf("NodeManager: dormant flavor list follows:\n"); 1001 dormant_addon_flavor_info *dafi; 1002 for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { 1003 printf(" AddonID %ld, AddonFlavorID %ld, MaxInstancesCount %ld, GlobalInstancesCount %ld, InfoValid %s\n", 1004 dafi->AddonID, dafi->AddonFlavorID, dafi->MaxInstancesCount, dafi->GlobalInstancesCount, dafi->InfoValid ? "yes" : "no"); 1005 if (!dafi->InfoValid) 1006 continue; 1007 printf(" addon-id %ld, addon-flavor-id %ld, addon-name \"%s\"\n", 1008 dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name); 1009 printf(" flavor-kinds %#08Lx, flavor_flags %#08lx, internal_id %ld, possible_count %ld, in_format_count %ld, out_format_count %ld\n", 1010 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); 1011 printf(" flavor-name \"%s\"\n", dafi->Info.name); 1012 printf(" flavor-info \"%s\"\n", dafi->Info.info); 1013 } 1014 printf("NodeManager: list end\n"); 1015 fDefaultManager->Dump(); 1016 } 1017 1018 const char * 1019 get_node_type(node_type t) 1020 { 1021 switch (t) { 1022 #define CASE(c) case c: return #c; 1023 CASE(VIDEO_INPUT) 1024 CASE(AUDIO_INPUT) 1025 CASE(VIDEO_OUTPUT) 1026 CASE(AUDIO_MIXER) 1027 CASE(AUDIO_OUTPUT) 1028 CASE(AUDIO_OUTPUT_EX) 1029 CASE(TIME_SOURCE) 1030 CASE(SYSTEM_TIME_SOURCE) 1031 default: return "unknown"; 1032 } 1033 }; 1034 1035