/* * Copyright (c) 2002, 2003 Marcus Overhagen * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files or portions * thereof (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject * to the following conditions: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice * in the binary, as well as this list of conditions and the following * disclaimer in the documentation and/or other materials provided with * the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #include "debug.h" #include "NodeManager.h" #include "DefaultManager.h" #include "AppManager.h" #include "MediaMisc.h" extern AppManager *gAppManager; const char *get_node_type(node_type t); NodeManager::NodeManager() : fNextAddOnID(1), fNextNodeID(1), fLocker(new BLocker("node manager locker")), fDormantAddonFlavorList(new List), fAddonPathMap(new Map), fRegisteredNodeMap(new Map), fDefaultManager(new DefaultManager) { } NodeManager::~NodeManager() { delete fLocker; delete fDormantAddonFlavorList; delete fAddonPathMap; delete fRegisteredNodeMap; delete fDefaultManager; } /********************************************************************** * Live node management **********************************************************************/ status_t 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) { BAutolock lock(fLocker); bool b; registered_node rn; rn.nodeid = fNextNodeID; rn.addon_id = addon_id; rn.addon_flavor_id = addon_flavor_id; strcpy(rn.name, name); rn.kinds = kinds; rn.port = port; rn.team = team; rn.creator = -1; // will be set later rn.globalrefcount = 1; rn.teamrefcount.Insert(team, 1); b = fRegisteredNodeMap->Insert(fNextNodeID, rn); ASSERT(b); *nodeid = fNextNodeID; fNextNodeID += 1; 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); return B_OK; } status_t NodeManager::UnregisterNode(media_addon_id *addonid, int32 *flavorid, media_node_id nodeid, team_id team) { BAutolock lock(fLocker); bool b; registered_node *rn; TRACE("NodeManager::UnregisterNode enter: node %ld, team %ld\n", nodeid, team); b = fRegisteredNodeMap->Get(nodeid, &rn); if (!b) { ERROR("NodeManager::UnregisterNode: couldn't find node %ld (team %ld)\n", nodeid, team); return B_ERROR; } if (rn->team != team) { ERROR("NodeManager::UnregisterNode: team %ld tried to unregister node %ld, but it was instantiated by team %ld\n", team, nodeid, rn->team); return B_ERROR; } if (rn->globalrefcount != 1) { ERROR("NodeManager::UnregisterNode: node %ld, team %ld has globalrefcount %ld (should be 1)\n", nodeid, team, rn->globalrefcount); //return B_ERROR; } *addonid = rn->addon_id; *flavorid = rn->addon_flavor_id; b = fRegisteredNodeMap->Remove(nodeid); ASSERT(b); TRACE("NodeManager::UnregisterNode leave: node %ld, addon_id %ld, flavor_id %ld team %ld\n", nodeid, *addonid, *flavorid, team); return B_OK; } status_t NodeManager::IncrementGlobalRefCount(media_node_id nodeid, team_id team) { BAutolock lock(fLocker); registered_node *rn; bool b; TRACE("NodeManager::IncrementGlobalRefCount enter: node %ld, team %ld\n", nodeid, team); b = fRegisteredNodeMap->Get(nodeid, &rn); if (!b) { ERROR("NodeManager::IncrementGlobalRefCount: node %ld not found\n", nodeid); return B_ERROR; } int32 *count; int32 debug_count; b = rn->teamrefcount.Get(team, &count); if (b) { *count += 1; debug_count = *count; } else { b = rn->teamrefcount.Insert(team, 1); ASSERT(b); debug_count = 1; } rn->globalrefcount += 1; TRACE("NodeManager::IncrementGlobalRefCount leave: node %ld, team %ld, count %ld, globalcount %ld\n", nodeid, team, debug_count, rn->globalrefcount); return B_OK; } status_t NodeManager::DecrementGlobalRefCount(media_node_id nodeid, team_id team) { BAutolock lock(fLocker); registered_node *rn; bool b; TRACE("NodeManager::DecrementGlobalRefCount enter: node %ld, team %ld\n", nodeid, team); b = fRegisteredNodeMap->Get(nodeid, &rn); if (!b) { ERROR("NodeManager::DecrementGlobalRefCount: node %ld not found\n", nodeid); return B_ERROR; } int32 *count; b = rn->teamrefcount.Get(team, &count); if (!b) { // Normally it is an error to release a node in another team. But we make one // exception. If the node is global, and the creator team tries to release it, // we will release it in the the media_addon_server. team_id addon_server_team; addon_server_team = gAppManager->AddonServerTeam(); if (rn->creator == team && rn->teamrefcount.Get(addon_server_team, &count)) { printf("!!! NodeManager::DecrementGlobalRefCount doing global release!\n"); rn->creator = -1; //invalidate! team = addon_server_team; //redirect! // the count variable was already redirected in if() statement above. } else { ERROR("NodeManager::DecrementGlobalRefCount: node %ld has no team %ld references\n", nodeid, team); return B_ERROR; } } *count -= 1; #if DEBUG >= 2 int32 debug_count = *count; #endif if (*count == 0) { b = rn->teamrefcount.Remove(team); ASSERT(b); } rn->globalrefcount -= 1; if (rn->globalrefcount == 0) { printf("NodeManager::DecrementGlobalRefCount: detected released node is now unused, node %ld\n", nodeid); FinalReleaseNode(nodeid); } TRACE("NodeManager::DecrementGlobalRefCount leave: node %ld, team %ld, count %ld, globalcount %ld\n", nodeid, team, debug_count, rn->globalrefcount); return B_OK; } status_t NodeManager::SetNodeCreator(media_node_id nodeid, team_id creator) { BAutolock lock(fLocker); registered_node *rn; bool b; TRACE("NodeManager::SetNodeCreator node %ld, creator %ld\n", nodeid, creator); b = fRegisteredNodeMap->Get(nodeid, &rn); if (!b) { ERROR("NodeManager::SetNodeCreator: node %ld not found\n", nodeid); return B_ERROR; } if (rn->creator != -1) { ERROR("NodeManager::SetNodeCreator: node %ld is already assigned creator %ld\n", nodeid, rn->creator); return B_ERROR; } rn->creator = creator; return B_OK; } void NodeManager::FinalReleaseNode(media_node_id nodeid) { BAutolock lock(fLocker); registered_node *rn; bool b; status_t rv; TRACE("NodeManager::FinalReleaseNode enter: node %ld\n", nodeid); b = fRegisteredNodeMap->Get(nodeid, &rn); if (!b) { ERROR("NodeManager::FinalReleaseNode: node %ld not found\n", nodeid); return; } node_final_release_command cmd; rv = SendToPort(rn->port, NODE_FINAL_RELEASE, &cmd, sizeof(cmd)); if (rv != B_OK) { ERROR("NodeManager::FinalReleaseNode: can't send command to node %ld\n", nodeid); return; } } status_t NodeManager::GetCloneForId(media_node *node, media_node_id nodeid, team_id team) { BAutolock lock(fLocker); registered_node *rn; bool b; TRACE("NodeManager::GetCloneForId enter: node %ld team %ld\n", nodeid, team); if (B_OK != IncrementGlobalRefCount(nodeid, team)) { ERROR("NodeManager::GetCloneForId: couldn't increment ref count, node %ld team %ld\n", nodeid, team); return B_ERROR; } b = fRegisteredNodeMap->Get(nodeid, &rn); if (!b) { ERROR("NodeManager::GetCloneForId: node %ld not found\n", nodeid); DecrementGlobalRefCount(nodeid, team); return B_ERROR; } node->node = rn->nodeid; node->port = rn->port; node->kind = rn->kinds; TRACE("NodeManager::GetCloneForId leave: node %ld team %ld\n", nodeid, team); return B_OK; } /* This function locates the default "node" for the requested "type" and returnes a clone. * If the requested type is AUDIO_OUTPUT_EX, also "input_name" and "input_id" need to be set and returned, * as this is required by BMediaRoster::GetAudioOutput(media_node *out_node, int32 *out_input_id, BString *out_input_name) */ status_t NodeManager::GetClone(media_node *node, char *input_name, int32 *input_id, node_type type, team_id team) { BAutolock lock(fLocker); status_t status; media_node_id id; TRACE("NodeManager::GetClone enter: team %ld, type %d (%s)\n", team, type, get_node_type(type)); status = GetDefaultNode(&id, input_name, input_id, type); if (status != B_OK) { ERROR("NodeManager::GetClone: couldn't GetDefaultNode, team %ld, type %d (%s)\n", team, type, get_node_type(type)); *node = media_node::null; return status; } ASSERT(id > 0); status = GetCloneForId(node, id, team); if (status != B_OK) { ERROR("NodeManager::GetClone: couldn't GetCloneForId, id %ld, team %ld, type %d (%s)\n", id, team, type, get_node_type(type)); *node = media_node::null; return status; } ASSERT(id == node->node); TRACE("NodeManager::GetClone leave: node id %ld, node port %ld, node kind %#lx\n", node->node, node->port, node->kind); return B_OK; } status_t NodeManager::ReleaseNode(const media_node &node, team_id team) { BAutolock lock(fLocker); TRACE("NodeManager::ReleaseNode enter: node %ld team %ld\n", node.node, team); if (B_OK != DecrementGlobalRefCount(node.node, team)) { ERROR("NodeManager::ReleaseNode: couldn't decrement node %ld team %ld ref count\n", node.node, team); } TRACE("NodeManager::ReleaseNode leave: node %ld team %ld\n", node.node, team); return B_OK; } status_t NodeManager::PublishInputs(const media_node &node, const media_input *inputs, int32 count) { BAutolock lock(fLocker); registered_node *rn; bool b; b = fRegisteredNodeMap->Get(node.node, &rn); if (!b) { ERROR("NodeManager::PublishInputs: node %ld not found\n", node.node); return B_ERROR; } rn->inputlist.MakeEmpty(); for (int32 i = 0; i < count; i++) rn->inputlist.Insert(inputs[i]); return B_OK; } status_t NodeManager::PublishOutputs(const media_node &node, const media_output *outputs, int32 count) { BAutolock lock(fLocker); registered_node *rn; bool b; b = fRegisteredNodeMap->Get(node.node, &rn); if (!b) { ERROR("NodeManager::PublishOutputs: node %ld not found\n", node.node); return B_ERROR; } rn->outputlist.MakeEmpty(); for (int32 i = 0; i < count; i++) rn->outputlist.Insert(outputs[i]); return B_OK; } status_t NodeManager::FindNodeId(media_node_id *nodeid, port_id port) { BAutolock lock(fLocker); registered_node *rn; for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { if (rn->port == port) { *nodeid = rn->nodeid; TRACE("NodeManager::FindNodeId found port %ld, node %ld\n", port, *nodeid); return B_OK; } media_output *output; for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { if (output->source.port == port) { *nodeid = rn->nodeid; TRACE("NodeManager::FindNodeId found output port %ld, node %ld\n", port, *nodeid); return B_OK; } } media_input *input; for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { if (input->destination.port == port) { *nodeid = rn->nodeid; TRACE("NodeManager::FindNodeId found input port %ld, node %ld\n", port, *nodeid); return B_OK; } } } ERROR("NodeManager::FindNodeId failed, port %ld\n", port); return B_ERROR; } status_t NodeManager::GetDormantNodeInfo(dormant_node_info *node_info, const media_node &node) { BAutolock lock(fLocker); // XXX not sure if this is correct registered_node *rn; for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { if (rn->nodeid == node.node) { if (rn->addon_id == -1 && node.node != NODE_SYSTEM_TIMESOURCE_ID) { // This function must return an error if the node is application owned 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); return B_ERROR; } ASSERT(node.port == rn->port); ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (rn->kinds & NODE_KIND_COMPARE_MASK)); node_info->addon = rn->addon_id; node_info->flavor_id = rn->addon_flavor_id; strcpy(node_info->name, rn->name); 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); return B_OK; } } ERROR("NodeManager::GetDormantNodeInfo failed, node %ld\n", node.node); return B_ERROR; } status_t NodeManager::GetLiveNodeInfo(live_node_info *live_info, const media_node &node) { BAutolock lock(fLocker); registered_node *rn; for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { if (rn->nodeid == node.node) { ASSERT(node.port == rn->port); ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (rn->kinds & NODE_KIND_COMPARE_MASK)); live_info->node = node; live_info->hint_point = BPoint(0, 0); strcpy(live_info->name, rn->name); TRACE("NodeManager::GetLiveNodeInfo node %ld, name = \"%s\"\n", node.node, rn->name); return B_OK; } } ERROR("NodeManager::GetLiveNodeInfo failed, node %ld\n", node.node); return B_ERROR; } status_t NodeManager::GetInstances(media_node_id *node_ids, int32* count, int32 maxcount, media_addon_id addon_id, int32 addon_flavor_id) { BAutolock lock(fLocker); registered_node *rn; *count = 0; for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) { if (rn->addon_id == addon_id && rn->addon_flavor_id == addon_flavor_id) { node_ids[*count] = rn->nodeid; *count += 1; maxcount -= 1; } } TRACE("NodeManager::GetInstances found %ld instances for addon_id %ld, addon_flavor_id %ld\n", *count, addon_id, addon_flavor_id); return B_OK; } status_t NodeManager::GetLiveNodes(Stack *livenodes, int32 maxcount, const media_format *inputformat /* = NULL */, const media_format *outputformat /* = NULL */, const char* name /* = NULL */, uint64 require_kinds /* = 0 */) { BAutolock lock(fLocker); registered_node *rn; int namelen; TRACE("NodeManager::GetLiveNodes: maxcount %ld, in-format %p, out-format %p, name %s, require_kinds 0x%Lx\n", maxcount, inputformat, outputformat, (name ? name : "NULL"), require_kinds); // determine the count of byte to compare when checking for a name with(out) wildcard if (name) { namelen = strlen(name); if (namelen > 0 && name[namelen - 1] == '*') namelen--; // compares without the '*' } else { namelen = 0; } for (fRegisteredNodeMap->Rewind(); (maxcount > 0) && fRegisteredNodeMap->GetNext(&rn); ) { if ((rn->kinds & require_kinds) != require_kinds) continue; if (namelen) { if (0 != strncmp(name, rn->name, namelen)) continue; } if (inputformat) { bool hasit = false; media_input *input; for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { if (format_is_compatible(*inputformat, input->format)) { hasit = true; break; } } if (!hasit) continue; } if (outputformat) { bool hasit = false; media_output *output; for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { if (format_is_compatible(*outputformat, output->format)) { hasit = true; break; } } if (!hasit) continue; } live_node_info lni; lni.node.node = rn->nodeid; lni.node.port = rn->port; lni.node.kind = rn->kinds; lni.hint_point = BPoint(0, 0); strcpy(lni.name, rn->name); livenodes->Push(lni); maxcount -= 1; } TRACE("NodeManager::GetLiveNodes found %ld\n", livenodes->CountItems()); return B_OK; } /* Add media_node_id of all live nodes to the message * int32 "media_node_id" (multiple items) */ status_t NodeManager::GetLiveNodes(BMessage *msg) { BAutolock lock(fLocker); registered_node *rn; for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { msg->AddInt32("media_node_id", rn->nodeid); } return B_OK; } /********************************************************************** * Registration of BMediaAddOns **********************************************************************/ void NodeManager::RegisterAddon(const entry_ref &ref, media_addon_id *newid) { BAutolock lock(fLocker); media_addon_id id; id = fNextAddOnID; fNextAddOnID += 1; printf("NodeManager::RegisterAddon: ref-name \"%s\", assigning id %ld\n", ref.name, id); fAddonPathMap->Insert(id, ref); *newid = id; } void NodeManager::UnregisterAddon(media_addon_id id) { BAutolock lock(fLocker); printf("NodeManager::UnregisterAddon: id %ld\n", id); RemoveDormantFlavorInfo(id); fAddonPathMap->Remove(id); } status_t NodeManager::GetAddonRef(entry_ref *ref, media_addon_id id) { BAutolock lock(fLocker); entry_ref *tempref; if (fAddonPathMap->Get(id, &tempref)) { *ref = *tempref; return B_OK; } return B_ERROR; } /********************************************************************** * Registration of node flavors, published by BMediaAddOns **********************************************************************/ // this function is only called (indirectly) by the media_addon_server void NodeManager::AddDormantFlavorInfo(const dormant_flavor_info &dfi) { BAutolock lock(fLocker); 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); // Try to find the addon-id/flavor-id in the list. // If it already exists, update the Info, but don't // change the GlobalInstancesCount dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { if (dafi->AddonID != dfi.node_info.addon || dafi->AddonFlavorID != dfi.node_info.flavor_id) continue; if (dafi->InfoValid) { ERROR("NodeManager::AddDormantFlavorInfo, addon-id %ld, flavor-id %ld does already exist\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id); } TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %ld, flavor-id %ld\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id); dafi->MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff; // do NOT modify dafi.GlobalInstancesCount dafi->InfoValid = true; dafi->Info = dfi; return; } // Insert information into the list { dormant_addon_flavor_info dafi; dafi.AddonID = dfi.node_info.addon; dafi.AddonFlavorID = dfi.node_info.flavor_id; dafi.MaxInstancesCount = dfi.possible_count > 0 ? dfi.possible_count : 0x7fffffff; dafi.GlobalInstancesCount = 0; dafi.InfoValid = true; dafi.Info = dfi; fDormantAddonFlavorList->Insert(dafi); } } // this function is only called (indirectly) by the media_addon_server void NodeManager::InvalidateDormantFlavorInfo(media_addon_id id) { BAutolock lock(fLocker); dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { if (dafi->AddonID == id && dafi->InfoValid == true) { 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); dormant_flavor_info dfi_null; dafi->Info = dfi_null; dafi->InfoValid = false; } } } // this function is only called by clean up after gone add-ons void NodeManager::RemoveDormantFlavorInfo(media_addon_id id) { BAutolock lock(fLocker); dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { if (dafi->AddonID == id) { 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); fDormantAddonFlavorList->RemoveCurrent(); } } } status_t NodeManager::IncrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team) { BAutolock lock(fLocker); dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid) continue; if (dafi->GlobalInstancesCount >= dafi->MaxInstancesCount) { ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld maximum (or more) instances already exist\n", addonid, flavorid); return B_ERROR; // maximum (or more) instances already exist } bool b; int32 *count; b = dafi->TeamInstancesCount.Get(team, &count); if (b) { *count += 1; } else { b = dafi->TeamInstancesCount.Insert(team, 1); ASSERT(b); } dafi->GlobalInstancesCount += 1; return B_OK; } ERROR("NodeManager::IncrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid); return B_ERROR; } status_t NodeManager::DecrementAddonFlavorInstancesCount(media_addon_id addonid, int32 flavorid, team_id team) { BAutolock lock(fLocker); dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { if (dafi->AddonID != addonid || dafi->AddonFlavorID != flavorid) continue; bool b; int32 *count; b = dafi->TeamInstancesCount.Get(team, &count); if (!b) { ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld team %ld has no references\n", addonid, flavorid, team); return B_ERROR; } *count -= 1; if (*count == 0) { b = dafi->TeamInstancesCount.Remove(team); ASSERT(b); } if (dafi->GlobalInstancesCount > 0) // avoid underflow dafi->GlobalInstancesCount -= 1; return B_OK; } ERROR("NodeManager::DecrementAddonFlavorInstancesCount addonid %ld, flavorid %ld not found\n", addonid, flavorid); return B_ERROR; } // this function is called when the media_addon_server has crashed void NodeManager::CleanupDormantFlavorInfos() { BAutolock lock(fLocker); printf("NodeManager::CleanupDormantFlavorInfos\n"); fDormantAddonFlavorList->MakeEmpty(); printf("NodeManager::CleanupDormantFlavorInfos done\n"); // XXX FlavorsChanged(media_addon_id addonid, int32 newcount, int32 gonecount) } status_t NodeManager::GetDormantNodes(dormant_node_info * out_info, int32 * io_count, const media_format * has_input /* = NULL */, const media_format * has_output /* = NULL */, const char * name /* = NULL */, uint64 require_kinds /* = NULL */, uint64 deny_kinds /* = NULL */) { BAutolock lock(fLocker); dormant_addon_flavor_info *dafi; int32 maxcount; int namelen; // determine the count of byte to compare when checking for a name with(out) wildcard if (name) { namelen = strlen(name); if (namelen > 0 && name[namelen - 1] == '*') namelen--; // compares without the '*' } else { namelen = 0; } maxcount = *io_count; *io_count = 0; for (fDormantAddonFlavorList->Rewind(); (*io_count < maxcount) && fDormantAddonFlavorList->GetNext(&dafi); ) { if (!dafi->InfoValid) continue; dormant_flavor_info *dfi; dfi = &dafi->Info; if ((dfi->kinds & require_kinds) != require_kinds) continue; if ((dfi->kinds & deny_kinds) != 0) continue; if (namelen) { if (0 != strncmp(name, dfi->name, namelen)) continue; } if (has_input) { bool hasit = false; for (int32 i = 0; i < dfi->in_format_count; i++) if (format_is_compatible(*has_input, dfi->in_formats[i])) { hasit = true; break; } if (!hasit) continue; } if (has_output) { bool hasit = false; for (int32 i = 0; i < dfi->out_format_count; i++) if (format_is_compatible(*has_output, dfi->out_formats[i])) { hasit = true; break; } if (!hasit) continue; } out_info[*io_count] = dfi->node_info; *io_count += 1; } return B_OK; } status_t NodeManager::GetDormantFlavorInfoFor(media_addon_id addon, int32 flavor_id, dormant_flavor_info *outFlavor) { BAutolock lock(fLocker); dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { if (dafi->AddonID == addon && dafi->AddonFlavorID == flavor_id && dafi->InfoValid == true) { *outFlavor = dafi->Info; return B_OK; } } return B_ERROR; } /********************************************************************** * Default node management **********************************************************************/ status_t NodeManager::SetDefaultNode(node_type type, const media_node *node, const dormant_node_info *info, const media_input *input) { BAutolock lock(fLocker); status_t err = B_BAD_VALUE; if (node) err = fDefaultManager->Set(node->node, NULL, 0, type); else if (input) err = fDefaultManager->Set(input->node.node, input->name, input->destination.id, type); else if (info) { media_node_id node_id; int32 count = 1; if ((err=GetInstances(&node_id, &count, count, info->addon, info->flavor_id))!=B_OK) return err; err = fDefaultManager->Set(node_id, NULL, 0, type); } if(err==B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) { fDefaultManager->SaveState(this); Dump(); } return err; } status_t NodeManager::GetDefaultNode(media_node_id *nodeid, char *input_name, int32 *input_id, node_type type) { BAutolock lock(fLocker); return fDefaultManager->Get(nodeid, input_name, input_id, type); } status_t NodeManager::RescanDefaultNodes() { BAutolock lock(fLocker); return fDefaultManager->Rescan(); } /********************************************************************** * Cleanup of dead teams **********************************************************************/ void NodeManager::CleanupTeam(team_id team) { BAutolock lock(fLocker); fDefaultManager->CleanupTeam(team); PRINT(1, "NodeManager::CleanupTeam: team %ld\n", team); // XXX send notifications after removing nodes // Cleanup node references registered_node *rn; for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { // if the gone team was the creator of some global dormant node instance, we now invalidate that // we may want to remove that global node, but I'm not sure if (rn->creator == team) { rn->creator = -1; // fall through } // if the team hosting this node is gone, remove node from database if (rn->team == team) { PRINT(1, "NodeManager::CleanupTeam: removing node id %ld, team %ld\n", rn->nodeid, team); fRegisteredNodeMap->RemoveCurrent(); continue; } // check the list of teams that have references to this node, and remove the team team_id *pteam; int32 *prefcount; for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&prefcount); ) { rn->teamrefcount.GetCurrentKey(&pteam); if (*pteam == team) { PRINT(1, "NodeManager::CleanupTeam: removing %ld refs from node id %ld, team %ld\n", *prefcount, rn->nodeid, team); rn->teamrefcount.RemoveCurrent(); break; } } // if the team refcount is now empty, also remove the node if (rn->teamrefcount.IsEmpty()) { PRINT(1, "NodeManager::CleanupTeam: removing node id %ld that has no teams\n", rn->nodeid); fRegisteredNodeMap->RemoveCurrent(); } } // Cleanup addon references dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { bool b; int32 *count; b = dafi->TeamInstancesCount.Get(team, &count); if (b) { PRINT(1, "NodeManager::CleanupTeam: removing %ld instances from addon %ld, flavor %ld\n", *count, dafi->AddonID, dafi->AddonFlavorID); dafi->GlobalInstancesCount -= *count; if (dafi->GlobalInstancesCount < 0) // avoid underflow dafi->GlobalInstancesCount = 0; b = dafi->TeamInstancesCount.Remove(team); ASSERT(b); } } } /********************************************************************** * State saving/loading **********************************************************************/ status_t NodeManager::LoadState() { BAutolock lock(fLocker); return fDefaultManager->LoadState(); } status_t NodeManager::SaveState() { BAutolock lock(fLocker); return fDefaultManager->SaveState(this); } /********************************************************************** * Debugging **********************************************************************/ void NodeManager::Dump() { BAutolock lock(fLocker); printf("\n"); /* for each addon-id, the addon path map contains an entry_ref */ printf("NodeManager: addon path map follows:\n"); entry_ref *ref; media_addon_id *id; for (fAddonPathMap->Rewind(); fAddonPathMap->GetNext(&ref); ) { fAddonPathMap->GetCurrentKey(&id); BPath path(ref); printf(" addon-id %ld, ref-name \"%s\", path \"%s\"\n", *id, ref->name, (path.InitCheck() == B_OK) ? path.Path() : "INVALID"); } printf("NodeManager: list end\n"); printf("\n"); /* for each node-id, the registered node map contians information about source of the node, users, etc. */ printf("NodeManager: registered nodes map follows:\n"); registered_node *rn; for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { printf(" node-id %ld, addon-id %ld, addon-flavor-id %ld, port %ld, creator %ld, team %ld, kinds %#08Lx, name \"%s\"\n", rn->nodeid, rn->addon_id, rn->addon_flavor_id, rn->port, rn->creator, rn->team, rn->kinds, rn->name); printf(" teams (refcount): "); team_id *team; int32 *refcount; for (rn->teamrefcount.Rewind(); rn->teamrefcount.GetNext(&refcount); ) { rn->teamrefcount.GetCurrentKey(&team); printf("%ld (%ld), ", *team, *refcount); } printf("\n"); media_input *input; for (rn->inputlist.Rewind(); rn->inputlist.GetNext(&input); ) { printf(" media_input: node-id %ld, node-port %ld, source-port %ld, source-id %ld, dest-port %ld, dest-id %ld, name \"%s\"\n", input->node.node, input->node.port, input->source.port, input->source.id, input->destination.port, input->destination.id, input->name); } if (rn->inputlist.IsEmpty()) printf(" media_input: none\n"); media_output *output; for (rn->outputlist.Rewind(); rn->outputlist.GetNext(&output); ) { printf(" media_output: node-id %ld, node-port %ld, source-port %ld, source-id %ld, dest-port %ld, dest-id %ld, name \"%s\"\n", output->node.node, output->node.port, output->source.port, output->source.id, output->destination.port, output->destination.id, output->name); } if (rn->outputlist.IsEmpty()) printf(" media_output: none\n"); } printf("NodeManager: list end\n"); printf("\n"); /* */ printf("NodeManager: dormant flavor list follows:\n"); dormant_addon_flavor_info *dafi; for (fDormantAddonFlavorList->Rewind(); fDormantAddonFlavorList->GetNext(&dafi); ) { printf(" AddonID %ld, AddonFlavorID %ld, MaxInstancesCount %ld, GlobalInstancesCount %ld, InfoValid %s\n", dafi->AddonID, dafi->AddonFlavorID, dafi->MaxInstancesCount, dafi->GlobalInstancesCount, dafi->InfoValid ? "yes" : "no"); if (!dafi->InfoValid) continue; printf(" addon-id %ld, addon-flavor-id %ld, addon-name \"%s\"\n", dafi->Info.node_info.addon, dafi->Info.node_info.flavor_id, dafi->Info.node_info.name); printf(" flavor-kinds %#08Lx, flavor_flags %#08lx, internal_id %ld, possible_count %ld, in_format_count %ld, out_format_count %ld\n", 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); printf(" flavor-name \"%s\"\n", dafi->Info.name); printf(" flavor-info \"%s\"\n", dafi->Info.info); } printf("NodeManager: list end\n"); fDefaultManager->Dump(); } const char * get_node_type(node_type t) { switch (t) { #define CASE(c) case c: return #c; CASE(VIDEO_INPUT) CASE(AUDIO_INPUT) CASE(VIDEO_OUTPUT) CASE(AUDIO_MIXER) CASE(AUDIO_OUTPUT) CASE(AUDIO_OUTPUT_EX) CASE(TIME_SOURCE) CASE(SYSTEM_TIME_SOURCE) default: return "unknown"; } };