/* * Copyright (c) 2015 Dario Casalinuovo * 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 "NodeManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "AppManager.h" #include "DefaultManager.h" #include "media_server.h" const char* get_node_type(node_type type) { #define CASE(c) case c: return #c; switch (type) { 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"; } #undef CASE } // #pragma mark - NodeManager::NodeManager() : BLocker("node manager"), fNextAddOnID(1), fNextNodeID(1), fDefaultManager(new DefaultManager) { } NodeManager::~NodeManager() { delete fDefaultManager; } // #pragma mark - Default node management status_t NodeManager::SetDefaultNode(node_type type, const media_node* node, const dormant_node_info* info, const media_input* input) { BAutolock _(this); status_t status = B_BAD_VALUE; if (node != NULL) status = fDefaultManager->Set(node->node, NULL, 0, type); else if (input != NULL) { status = fDefaultManager->Set(input->node.node, input->name, input->destination.id, type); } else if (info != NULL) { media_node_id nodeID; int32 count = 1; status = GetInstances(info->addon, info->flavor_id, &nodeID, &count, count); if (status == B_OK) status = fDefaultManager->Set(nodeID, NULL, 0, type); } if (status == B_OK && (type == VIDEO_INPUT || type == VIDEO_OUTPUT || type == AUDIO_OUTPUT || type == AUDIO_INPUT)) { fDefaultManager->SaveState(this); Dump(); } return status; } status_t NodeManager::GetDefaultNode(node_type type, media_node_id* _nodeID, char* inputName, int32* _inputID) { BAutolock _(this); return fDefaultManager->Get(_nodeID, inputName, _inputID, type); } status_t NodeManager::RescanDefaultNodes() { BAutolock _(this); return fDefaultManager->Rescan(); } // #pragma mark - Live node management status_t NodeManager::RegisterNode(media_addon_id addOnID, int32 flavorID, const char* name, uint64 kinds, port_id port, team_id team, media_node_id timesource, media_node_id* _nodeID) { BAutolock _(this); registered_node node; node.timesource_id = timesource; node.add_on_id = addOnID; node.flavor_id = flavorID; strlcpy(node.name, name, sizeof(node.name)); node.kinds = kinds; node.port = port; node.containing_team = team; node.creator = -1; // will be set later node.ref_count = 1; if ((node.kinds & B_TIME_SOURCE) != 0 && strcmp(node.name, "System clock") == 0) { // This may happen when media_addon_server crash, // we will replace the old timesource. node.node_id = NODE_SYSTEM_TIMESOURCE_ID; NodeMap::iterator found = fNodeMap.find(node.node_id); if (found != fNodeMap.end()) fNodeMap.erase(node.node_id); *_nodeID = node.node_id; } else { node.node_id = fNextNodeID; *_nodeID = node.node_id; } try { node.team_ref_count.insert(std::make_pair(team, 1)); fNodeMap.insert(std::make_pair(node.node_id, node)); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } fNextNodeID++; TRACE("NodeManager::RegisterNode: node %" B_PRId32 ", addon_id %" B_PRId32 ", flavor_id %" B_PRId32 ", name \"%s\", kinds %#" B_PRIx64", port %" B_PRId32 ", team %" B_PRId32 "\n", *_nodeID, addOnID, flavorID, name, kinds, port, team); return B_OK; } status_t NodeManager::UnregisterNode(media_node_id id, team_id team, media_addon_id* _addOnID, int32* _flavorID) { TRACE("NodeManager::UnregisterNode enter: node %" B_PRId32 ", team %" B_PRId32 "\n", id, team); BAutolock _(this); NodeMap::iterator found = fNodeMap.find(id); if (found == fNodeMap.end()) { ERROR("NodeManager::UnregisterNode: couldn't find node %" B_PRId32 " (team %" B_PRId32 ")\n", id, team); return B_ERROR; } registered_node& node = found->second; if (node.containing_team != team) { ERROR("NodeManager::UnregisterNode: team %" B_PRId32 " tried to " "unregister node %" B_PRId32 ", but it was instantiated by team %" B_PRId32 "\n", team, id, node.containing_team); return B_ERROR; } if (node.ref_count != 1) { ERROR("NodeManager::UnregisterNode: node %" B_PRId32 ", team %" B_PRId32 " has ref count %" B_PRId32 " (should be 1)\n", id, team, node.ref_count); //return B_ERROR; } if (_addOnID != NULL) *_addOnID = node.add_on_id; if (_flavorID != NULL) *_flavorID = node.flavor_id; fNodeMap.erase(found); TRACE("NodeManager::UnregisterNode leave: node %" B_PRId32 ", addon_id %" B_PRId32 ", flavor_id %" B_PRId32 " team %" B_PRId32 "\n", id, *_addOnID, *_flavorID, team); return B_OK; } status_t NodeManager::ReleaseNodeReference(media_node_id id, team_id team) { TRACE("NodeManager::ReleaseNodeReference enter: node %" B_PRId32 ", team %" B_PRId32 "\n", id, team); BAutolock _(this); NodeMap::iterator found = fNodeMap.find(id); if (found == fNodeMap.end()) { ERROR("NodeManager::ReleaseNodeReference: node %" B_PRId32 " not " "found\n", id); return B_ERROR; } registered_node& node = found->second; TeamCountMap::iterator teamRef = node.team_ref_count.find(team); if (teamRef == node.team_ref_count.end()) { // 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 addOnServer = gAppManager->AddOnServerTeam(); teamRef = node.team_ref_count.find(addOnServer); if (node.creator == team && teamRef != node.team_ref_count.end()) { PRINT(1, "!!! NodeManager::ReleaseNodeReference doing global " "release!\n"); node.creator = -1; // invalidate! team = addOnServer; } else { ERROR("NodeManager::ReleaseNodeReference: node %" B_PRId32 " has " "no team %" B_PRId32 " references\n", id, team); return B_ERROR; } } #if DEBUG int32 teamCount = teamRef->second - 1; (void)teamCount; #endif if (--teamRef->second == 0) node.team_ref_count.erase(teamRef); if (--node.ref_count == 0) { PRINT(1, "NodeManager::ReleaseNodeReference: detected released node is" " now unused, node %" B_PRId32 "\n", id); // TODO: remove! node_final_release_command command; status_t status = SendToPort(node.port, NODE_FINAL_RELEASE, &command, sizeof(command)); if (status != B_OK) { ERROR("NodeManager::ReleaseNodeReference: can't send command to " "node %" B_PRId32 "\n", id); // ignore error } } TRACE("NodeManager::ReleaseNodeReference leave: node %" B_PRId32 ", team %" B_PRId32 ", ref %" B_PRId32 ", team ref %" B_PRId32 "\n", id, team, node.ref_count, teamCount); return B_OK; } status_t NodeManager::ReleaseNodeAll(media_node_id id) { TRACE("NodeManager::ReleaseNodeAll enter: node %" B_PRId32 "\n", id); BAutolock _(this); NodeMap::iterator found = fNodeMap.find(id); if (found == fNodeMap.end()) { ERROR("NodeManager::ReleaseNodeAll: node %" B_PRId32 " not found\n", id); return B_ERROR; } registered_node& node = found->second; node.team_ref_count.clear(); node.ref_count = 0; node_final_release_command command; status_t status = SendToPort(node.port, NODE_FINAL_RELEASE, &command, sizeof(command)); if (status != B_OK) { ERROR("NodeManager::ReleaseNodeAll: can't send command to " "node %" B_PRId32 "\n", id); // ignore error } TRACE("NodeManager::ReleaseNodeAll leave: node %" B_PRId32 "\n", id); return B_OK; } status_t NodeManager::SetNodeCreator(media_node_id id, team_id creator) { TRACE("NodeManager::SetNodeCreator node %" B_PRId32 ", creator %" B_PRId32 "\n", id, creator); BAutolock _(this); NodeMap::iterator found = fNodeMap.find(id); if (found == fNodeMap.end()) { ERROR("NodeManager::SetNodeCreator: node %" B_PRId32 " not found\n", id); return B_ERROR; } registered_node& node = found->second; if (node.creator != -1) { ERROR("NodeManager::SetNodeCreator: node %" B_PRId32 " is already" " assigned creator %" B_PRId32 "\n", id, node.creator); return B_ERROR; } node.creator = creator; return B_OK; } status_t NodeManager::GetCloneForID(media_node_id id, team_id team, media_node* node) { TRACE("NodeManager::GetCloneForID enter: node %" B_PRId32 " team %" B_PRId32 "\n", id, team); BAutolock _(this); status_t status = _AcquireNodeReference(id, team); if (status != B_OK) { ERROR("NodeManager::GetCloneForID: couldn't increment ref count, " "node %" B_PRId32 " team %" B_PRId32 "\n", id, team); return status; } NodeMap::iterator found = fNodeMap.find(id); if (found == fNodeMap.end()) { ERROR("NodeManager::GetCloneForID: node %" B_PRId32 " not found\n", id); return B_ERROR; } registered_node& registeredNode = found->second; node->node = registeredNode.node_id; node->port = registeredNode.port; node->kind = registeredNode.kinds; TRACE("NodeManager::GetCloneForID leave: node %" B_PRId32 " team %" B_PRId32 "\n", id, team); return B_OK; } /*! This function locates the default "node" for the requested "type" and returns 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(node_type type, team_id team, media_node* node, char* inputName, int32* _inputID) { BAutolock _(this); TRACE("NodeManager::GetClone enter: team %" B_PRId32 ", type %d (%s)\n", team, type, get_node_type(type)); media_node_id id; status_t status = GetDefaultNode(type, &id, inputName, _inputID); if (status != B_OK) { ERROR("NodeManager::GetClone: couldn't GetDefaultNode, team %" B_PRId32 ", type %d (%s)\n", team, type, get_node_type(type)); *node = media_node::null; return status; } ASSERT(id > 0); status = GetCloneForID(id, team, node); if (status != B_OK) { ERROR("NodeManager::GetClone: couldn't GetCloneForID, id %" B_PRId32 ", team %" B_PRId32 ", 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 %" B_PRId32 ", node port %" B_PRId32 ", node kind %#" B_PRIx64 "\n", node->node, node->port, node->kind); return B_OK; } status_t NodeManager::ReleaseNode(const media_node& node, team_id team) { TRACE("NodeManager::ReleaseNode enter: node %" B_PRId32 " team %" B_PRId32 "\n", node.node, team); if (ReleaseNodeReference(node.node, team) != B_OK) { ERROR("NodeManager::ReleaseNode: couldn't decrement node %" B_PRId32 " team %" B_PRId32 " ref count\n", node.node, team); } return B_OK; } status_t NodeManager::PublishInputs(const media_node& node, const media_input* inputs, int32 count) { BAutolock _(this); NodeMap::iterator found = fNodeMap.find(node.node); if (found == fNodeMap.end()) { ERROR("NodeManager::PublishInputs: node %" B_PRId32 " not found\n", node.node); return B_ERROR; } registered_node& registeredNode = found->second; registeredNode.input_list.clear(); try { for (int32 i = 0; i < count; i++) registeredNode.input_list.push_back(inputs[i]); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } return B_OK; } status_t NodeManager::PublishOutputs(const media_node &node, const media_output* outputs, int32 count) { BAutolock _(this); NodeMap::iterator found = fNodeMap.find(node.node); if (found == fNodeMap.end()) { ERROR("NodeManager::PublishOutputs: node %" B_PRId32 " not found\n", node.node); return B_ERROR; } registered_node& registeredNode = found->second; registeredNode.output_list.clear(); try { for (int32 i = 0; i < count; i++) registeredNode.output_list.push_back(outputs[i]); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } return B_OK; } status_t NodeManager::FindNodeID(port_id port, media_node_id* _id) { BAutolock _(this); NodeMap::iterator iterator = fNodeMap.begin(); for (; iterator != fNodeMap.end(); iterator++) { registered_node& node = iterator->second; if (node.port == port) { *_id = node.node_id; TRACE("NodeManager::FindNodeID found port %" B_PRId32 ", node %" B_PRId32 "\n", port, node.node_id); return B_OK; } OutputList::iterator outIterator = node.output_list.begin(); for (; outIterator != node.output_list.end(); outIterator++) { if (outIterator->source.port == port) { *_id = node.node_id; TRACE("NodeManager::FindNodeID found output port %" B_PRId32 ", node %" B_PRId32 "\n", port, node.node_id); return B_OK; } } InputList::iterator inIterator = node.input_list.begin(); for (; inIterator != node.input_list.end(); inIterator++) { if (inIterator->destination.port == port) { *_id = node.node_id; TRACE("NodeManager::FindNodeID found input port %" B_PRId32 ", node %" B_PRId32 "\n", port, node.node_id); return B_OK; } } } ERROR("NodeManager::FindNodeID failed, port %" B_PRId32 "\n", port); return B_ERROR; } status_t NodeManager::GetDormantNodeInfo(const media_node& node, dormant_node_info* nodeInfo) { // TODO: not sure if this is correct BAutolock _(this); NodeMap::iterator found = fNodeMap.find(node.node); if (found == fNodeMap.end()) { ERROR("NodeManager::GetDormantNodeInfo: node %" B_PRId32 " not found" "\n", node.node); return B_ERROR; } registered_node& registeredNode = found->second; if (registeredNode.add_on_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 %" B_PRId32 ", add_on_id %" B_PRId32 ", flavor_id %" B_PRId32 ", name \"%s\"\n", node.node, registeredNode.add_on_id, registeredNode.flavor_id, registeredNode.name); return B_ERROR; } ASSERT(node.port == registeredNode.port); ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (registeredNode.kinds & NODE_KIND_COMPARE_MASK)); nodeInfo->addon = registeredNode.add_on_id; nodeInfo->flavor_id = registeredNode.flavor_id; strlcpy(nodeInfo->name, registeredNode.name, sizeof(nodeInfo->name)); TRACE("NodeManager::GetDormantNodeInfo node %" B_PRId32 ", add_on_id %" B_PRId32 ", flavor_id %" B_PRId32 ", name \"%s\"\n", node.node, registeredNode.add_on_id, registeredNode.flavor_id, registeredNode.name); return B_OK; } status_t NodeManager::GetLiveNodeInfo(const media_node& node, live_node_info* liveInfo) { BAutolock _(this); NodeMap::iterator found = fNodeMap.find(node.node); if (found == fNodeMap.end()) { ERROR("NodeManager::GetLiveNodeInfo: node %" B_PRId32 " not found\n", node.node); return B_ERROR; } registered_node& registeredNode = found->second; ASSERT(node.port == registeredNode.port); ASSERT((node.kind & NODE_KIND_COMPARE_MASK) == (registeredNode.kinds & NODE_KIND_COMPARE_MASK)); liveInfo->node = node; liveInfo->hint_point = BPoint(0, 0); strlcpy(liveInfo->name, registeredNode.name, sizeof(liveInfo->name)); TRACE("NodeManager::GetLiveNodeInfo node %" B_PRId32 ", name = \"%s\"\n", node.node, registeredNode.name); return B_OK; } status_t NodeManager::GetInstances(media_addon_id addOnID, int32 flavorID, media_node_id* ids, int32* _count, int32 maxCount) { BAutolock _(this); NodeMap::iterator iterator = fNodeMap.begin(); int32 count = 0; for (; iterator != fNodeMap.end() && count < maxCount; iterator++) { registered_node& node = iterator->second; if (node.add_on_id == addOnID && node.flavor_id == flavorID) ids[count++] = node.node_id; } TRACE("NodeManager::GetInstances found %" B_PRId32 " instances for " "addon_id %" B_PRId32 ", flavor_id %" B_PRId32 "\n", count, addOnID, flavorID); *_count = count; return B_OK; } status_t NodeManager::GetLiveNodes(LiveNodeList& liveNodes, int32 maxCount, const media_format* inputFormat, const media_format* outputFormat, const char* name, uint64 requireKinds) { TRACE("NodeManager::GetLiveNodes: maxCount %" B_PRId32 ", in-format %p, " "out-format %p, name %s, require kinds 0x%" B_PRIx64 "\n", maxCount, inputFormat, outputFormat, name != NULL ? name : "NULL", requireKinds); BAutolock _(this); // Determine the count of byte to compare when checking for a name with // or without wildcard size_t nameLength = 0; if (name != NULL) { nameLength = strlen(name); if (nameLength > 0 && name[nameLength - 1] == '*') nameLength--; } NodeMap::iterator iterator = fNodeMap.begin(); int32 count = 0; for (; iterator != fNodeMap.end() && count < maxCount; iterator++) { registered_node& node = iterator->second; if ((node.kinds & requireKinds) != requireKinds) continue; if (nameLength != 0) { if (strncmp(name, node.name, nameLength) != 0) continue; } if (inputFormat != NULL) { bool found = false; for (InputList::iterator inIterator = node.input_list.begin(); inIterator != node.input_list.end(); inIterator++) { media_input& input = *inIterator; if (format_is_compatible(*inputFormat, input.format)) { found = true; break; } } if (!found) continue; } if (outputFormat != NULL) { bool found = false; for (OutputList::iterator outIterator = node.output_list.begin(); outIterator != node.output_list.end(); outIterator++) { media_output& output = *outIterator; if (format_is_compatible(*outputFormat, output.format)) { found = true; break; } } if (!found) continue; } live_node_info info; info.node.node = node.node_id; info.node.port = node.port; info.node.kind = node.kinds; info.hint_point = BPoint(0, 0); strlcpy(info.name, node.name, sizeof(info.name)); try { liveNodes.push_back(info); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } count++; } TRACE("NodeManager::GetLiveNodes found %" B_PRId32 "\n", count); 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* message) { BAutolock _(this); NodeMap::iterator iterator = fNodeMap.begin(); for (; iterator != fNodeMap.end(); iterator++) { registered_node& node = iterator->second; if (message->AddInt32("media_node_id", node.node_id) != B_OK) return B_NO_MEMORY; } return B_OK; } // #pragma mark - Registration of BMediaAddOns void NodeManager::RegisterAddOn(const entry_ref& ref, media_addon_id* _newID) { BAutolock _(this); media_addon_id id = fNextAddOnID++; // printf("NodeManager::RegisterAddOn: ref-name \"%s\", assigning id %" // B_PRId32 "\n", ref.name, id); try { fPathMap.insert(std::make_pair(id, ref)); *_newID = id; } catch (std::bad_alloc& exception) { *_newID = -1; } } void NodeManager::UnregisterAddOn(media_addon_id addOnID) { PRINT(1, "NodeManager::UnregisterAddOn: id %" B_PRId32 "\n", addOnID); BAutolock _(this); RemoveDormantFlavorInfo(addOnID); fPathMap.erase(addOnID); } status_t NodeManager::GetAddOnRef(media_addon_id addOnID, entry_ref* ref) { BAutolock _(this); PathMap::iterator found = fPathMap.find(addOnID); if (found == fPathMap.end()) return B_ERROR; *ref = found->second; return B_OK; } // #pragma mark - Registration of node flavors, published by BMediaAddOns //! This function is only used (indirectly) by the media_addon_server. status_t NodeManager::AddDormantFlavorInfo(const dormant_flavor_info& flavorInfo) { PRINT(1, "NodeManager::AddDormantFlavorInfo, addon-id %" B_PRId32 ", " "flavor-id %" B_PRId32 ", name \"%s\", flavor-name \"%s\", flavor-info" " \"%s\"\n", flavorInfo.node_info.addon, flavorInfo.node_info.flavor_id, flavorInfo.node_info.name, flavorInfo.name, flavorInfo.info); BAutolock _(this); // Try to find the addon-id/flavor-id in the list. // If it already exists, update the info, but don't change its instance // count. for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& info = *iterator; if (info.add_on_id != flavorInfo.node_info.addon || info.flavor_id != flavorInfo.node_info.flavor_id) continue; if (info.info_valid) { ERROR("NodeManager::AddDormantFlavorInfo, addon-id %" B_PRId32 ", " "flavor-id %" B_PRId32 " does already exist\n", info.info.node_info.addon, info.info.node_info.flavor_id); } TRACE("NodeManager::AddDormantFlavorInfo, updating addon-id %" B_PRId32 ", flavor-id %" B_PRId32 "\n", info.info.node_info.addon, info.info.node_info.flavor_id); info.max_instances_count = flavorInfo.possible_count > 0 ? flavorInfo.possible_count : INT32_MAX; info.info_valid = true; info.info = flavorInfo; return B_OK; } // Insert information into the list dormant_add_on_flavor_info info; info.add_on_id = flavorInfo.node_info.addon; info.flavor_id = flavorInfo.node_info.flavor_id; info.max_instances_count = flavorInfo.possible_count > 0 ? flavorInfo.possible_count : INT32_MAX; info.instances_count = 0; info.info_valid = true; info.info = flavorInfo; try { fDormantFlavors.push_back(info); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } return B_OK; } //! This function is only used (indirectly) by the media_addon_server void NodeManager::InvalidateDormantFlavorInfo(media_addon_id addOnID) { BAutolock _(this); for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& info = *iterator; if (info.add_on_id == addOnID && info.info_valid) { PRINT(1, "NodeManager::InvalidateDormantFlavorInfo, addon-id %" B_PRId32 ", flavor-id %" B_PRId32 ", name \"%s\", flavor-name " "\"%s\", flavor-info \"%s\"\n", info.info.node_info.addon, info.info.node_info.flavor_id, info.info.node_info.name, info.info.name, info.info.info); info.info_valid = false; } } } //! This function is only used (indirectly) by the media_addon_server void NodeManager::RemoveDormantFlavorInfo(media_addon_id addOnID) { BAutolock _(this); for (size_t index = 0; index < fDormantFlavors.size(); index++) { dormant_add_on_flavor_info& info = fDormantFlavors[index]; if (info.add_on_id == addOnID) { PRINT(1, "NodeManager::RemoveDormantFlavorInfo, addon-id %" B_PRId32 ", flavor-id %" B_PRId32 ", name \"%s\", flavor-name " "\"%s\", flavor-info \"%s\"\n", info.info.node_info.addon, info.info.node_info.flavor_id, info.info.node_info.name, info.info.name, info.info.info); fDormantFlavors.erase(fDormantFlavors.begin() + index--); } } } status_t NodeManager::IncrementFlavorInstancesCount(media_addon_id addOnID, int32 flavorID, team_id team) { BAutolock _(this); for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& info = *iterator; if (info.add_on_id != addOnID || info.flavor_id != flavorID) continue; if (info.instances_count >= info.max_instances_count) { // maximum (or more) instances already exist ERROR("NodeManager::IncrementFlavorInstancesCount addon-id %" B_PRId32 ", flavor-id %" B_PRId32 " maximum (or more) " "instances already exist\n", addOnID, flavorID); return B_ERROR; } TeamCountMap::iterator teamInstance = info.team_instances_count.find(team); if (teamInstance == info.team_instances_count.end()) { // This is the team's first instance try { info.team_instances_count.insert(std::make_pair(team, 1)); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } } else { // Just increase its ref count teamInstance->second++; } info.instances_count++; return B_OK; } ERROR("NodeManager::IncrementFlavorInstancesCount addon-id %" B_PRId32 ", " "flavor-id %" B_PRId32 " not found\n", addOnID, flavorID); return B_ERROR; } status_t NodeManager::DecrementFlavorInstancesCount(media_addon_id addOnID, int32 flavorID, team_id team) { BAutolock _(this); for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& info = *iterator; if (info.add_on_id != addOnID || info.flavor_id != flavorID) continue; TeamCountMap::iterator teamInstance = info.team_instances_count.find(team); if (teamInstance == info.team_instances_count.end()) { ERROR("NodeManager::DecrementFlavorInstancesCount addon-id %" B_PRId32 ", flavor-id %" B_PRId32 " team %" B_PRId32 " has no " "references\n", addOnID, flavorID, team); return B_ERROR; } if (--teamInstance->second == 0) info.team_instances_count.erase(teamInstance); info.instances_count--; return B_OK; } ERROR("NodeManager::DecrementFlavorInstancesCount addon-id %" B_PRId32 ", " "flavor-id %" B_PRId32 " not found\n", addOnID, flavorID); return B_ERROR; } //! This function is called when the media_addon_server has crashed void NodeManager::CleanupDormantFlavorInfos() { PRINT(1, "NodeManager::CleanupDormantFlavorInfos\n"); BAutolock _(this); for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& info = *iterator; // Current instance count is zero since the media_addon_server crashed. BPrivate::media::notifications::FlavorsChanged(info.add_on_id, 0, info.instances_count); } fDormantFlavors.clear(); PRINT(1, "NodeManager::CleanupDormantFlavorInfos done\n"); } status_t NodeManager::GetDormantNodes(dormant_node_info* infos, int32* _count, const media_format* input, const media_format* output, const char* name, uint64 requireKinds, uint64 denyKinds) { BAutolock _(this); // Determine the count of byte to compare when checking for a name with // or without wildcard size_t nameLength = 0; if (name != NULL) { nameLength = strlen(name); if (nameLength > 0 && name[nameLength - 1] == '*') nameLength--; } int32 maxCount = *_count; int32 count = 0; for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end() && count < maxCount; iterator++) { dormant_add_on_flavor_info& info = *iterator; if (!info.info_valid) continue; if ((info.info.kinds & requireKinds) != requireKinds || (info.info.kinds & denyKinds) != 0) continue; if (nameLength != 0) { if (strncmp(name, info.info.name, nameLength) != 0) continue; } if (input != NULL) { bool found = false; for (int32 i = 0; i < info.info.in_format_count; i++) { if (format_is_compatible(*input, info.info.in_formats[i])) { found = true; break; } } if (!found) continue; } if (output != NULL) { bool found = false; for (int32 i = 0; i < info.info.out_format_count; i++) { if (format_is_compatible(*output, info.info.out_formats[i])) { found = true; break; } } if (!found) continue; } infos[count++] = info.info.node_info; } *_count = count; return B_OK; } status_t NodeManager::GetDormantFlavorInfoFor(media_addon_id addOnID, int32 flavorID, dormant_flavor_info* flavorInfo) { BAutolock _(this); for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& info = *iterator; if (info.add_on_id == addOnID && info.flavor_id == flavorID && info.info_valid) { *flavorInfo = info.info; return B_OK; } } return B_ERROR; } // #pragma mark - Misc. status_t NodeManager::SetNodeTimeSource(media_node_id node, media_node_id timesource) { BAutolock _(this); NodeMap::iterator found = fNodeMap.find(node); if (found == fNodeMap.end()) { ERROR("NodeManager::SetNodeTimeSource: node %" B_PRId32 " not found\n", node); return B_ERROR; } registered_node& registeredNode = found->second; registeredNode.timesource_id = timesource; return B_OK; } void NodeManager::CleanupTeam(team_id team) { BAutolock _(this); fDefaultManager->CleanupTeam(team); PRINT(1, "NodeManager::CleanupTeam: team %" B_PRId32 "\n", team); // Cleanup node references for (NodeMap::iterator iterator = fNodeMap.begin(); iterator != fNodeMap.end();) { registered_node& node = iterator->second; NodeMap::iterator remove = iterator++; // 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 (node.creator == team) { node.creator = -1; // fall through } // If the team hosting this node is gone, remove node from database if (node.containing_team == team) { PRINT(1, "NodeManager::CleanupTeam: removing node id %" B_PRId32 ", team %" B_PRId32 "\n", node.node_id, team); // Ensure the slave node is removed from it's timesource _NotifyTimeSource(node); fNodeMap.erase(remove); BPrivate::media::notifications::NodesDeleted(&node.node_id, 1); continue; } // Check the list of teams that have references to this node, and // remove the team TeamCountMap::iterator teamRef = node.team_ref_count.find(team); if (teamRef != node.team_ref_count.end()) { PRINT(1, "NodeManager::CleanupTeam: removing %" B_PRId32 " refs " "from node id %" B_PRId32 ", team %" B_PRId32 "\n", teamRef->second, node.node_id, team); node.ref_count -= teamRef->second; if (node.ref_count == 0) { PRINT(1, "NodeManager::CleanupTeam: removing node id %" B_PRId32 " that has no teams\n", node.node_id); // Ensure the slave node is removed from it's timesource _NotifyTimeSource(node); fNodeMap.erase(remove); BPrivate::media::notifications::NodesDeleted(&node.node_id, 1); } else node.team_ref_count.erase(teamRef); } } // Cleanup add-on references for (size_t index = 0; index < fDormantFlavors.size(); index++) { dormant_add_on_flavor_info& flavorInfo = fDormantFlavors[index]; TeamCountMap::iterator instanceCount = flavorInfo.team_instances_count.find(team); if (instanceCount != flavorInfo.team_instances_count.end()) { PRINT(1, "NodeManager::CleanupTeam: removing %" B_PRId32 " " "instances from addon %" B_PRId32 ", flavor %" B_PRId32 "\n", instanceCount->second, flavorInfo.add_on_id, flavorInfo.flavor_id); int32 count = flavorInfo.instances_count; flavorInfo.instances_count -= instanceCount->second; if (flavorInfo.instances_count <= 0) { fDormantFlavors.erase(fDormantFlavors.begin() + index--); BPrivate::media::notifications::FlavorsChanged( flavorInfo.add_on_id, 0, count); } else flavorInfo.team_instances_count.erase(team); } } } status_t NodeManager::LoadState() { BAutolock _(this); return fDefaultManager->LoadState(); } status_t NodeManager::SaveState() { BAutolock _(this); return fDefaultManager->SaveState(this); } void NodeManager::Dump() { BAutolock _(this); // for each addon-id, the add-on path map contains an entry_ref printf("\nNodeManager: addon path map follows:\n"); for (PathMap::iterator iterator = fPathMap.begin(); iterator != fPathMap.end(); iterator++) { BPath path(&iterator->second); printf(" addon-id %" B_PRId32 ", path \"%s\"\n", iterator->first, path.InitCheck() == B_OK ? path.Path() : "INVALID"); } printf("NodeManager: list end\n\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"); for (NodeMap::iterator iterator = fNodeMap.begin(); iterator != fNodeMap.end(); iterator++) { registered_node& node = iterator->second; printf(" node-id %" B_PRId32 ", addon-id %" B_PRId32 ", addon-flavor-" "id %" B_PRId32 ", port %" B_PRId32 ", creator %" B_PRId32 ", " "team %" B_PRId32 ", kinds %#08" B_PRIx64 ", name \"%s\", " "ref_count %" B_PRId32 "\n", node.node_id, node.add_on_id, node.flavor_id, node.port, node.creator, node.containing_team, node.kinds, node.name, node.ref_count); printf(" teams (refcount): "); for (TeamCountMap::iterator refsIterator = node.team_ref_count.begin(); refsIterator != node.team_ref_count.end(); refsIterator++) { printf("%" B_PRId32 " (%" B_PRId32 "), ", refsIterator->first, refsIterator->second); } printf("\n"); for (InputList::iterator inIterator = node.input_list.begin(); inIterator != node.input_list.end(); inIterator++) { media_input& input = *inIterator; printf(" media_input: node-id %" B_PRId32 ", node-port %" B_PRId32 ", source-port %" B_PRId32 ", source-id %" B_PRId32 ", dest-port %" B_PRId32 ", dest-id %" B_PRId32 ", name " "\"%s\"\n", input.node.node, input.node.port, input.source.port, input.source.id, input.destination.port, input.destination.id, input.name); } if (node.input_list.empty()) printf(" media_input: none\n"); for (OutputList::iterator outIterator = node.output_list.begin(); outIterator != node.output_list.end(); outIterator++) { media_output& output = *outIterator; printf(" media_output: node-id %" B_PRId32 ", node-port %" B_PRId32 ", source-port %" B_PRId32 ", source-id %" B_PRId32 ", dest-port %" B_PRId32 ", dest-id %" B_PRId32 ", name " "\"%s\"\n", output.node.node, output.node.port, output.source.port, output.source.id, output.destination.port, output.destination.id, output.name); } if (node.output_list.empty()) printf(" media_output: none\n"); } printf("NodeManager: list end\n"); printf("\n"); // Dormant add-on flavors printf("NodeManager: dormant flavor list follows:\n"); for (DormantFlavorList::iterator iterator = fDormantFlavors.begin(); iterator != fDormantFlavors.end(); iterator++) { dormant_add_on_flavor_info& flavorInfo = *iterator; printf(" addon-id %" B_PRId32 ", flavor-id %" B_PRId32 ", max " "instances count %" B_PRId32 ", instances count %" B_PRId32 ", " "info valid %s\n", flavorInfo.add_on_id, flavorInfo.flavor_id, flavorInfo.max_instances_count, flavorInfo.instances_count, flavorInfo.info_valid ? "yes" : "no"); printf(" teams (instances): "); for (TeamCountMap::iterator countIterator = flavorInfo.team_instances_count.begin(); countIterator != flavorInfo.team_instances_count.end(); countIterator++) { printf("%" B_PRId32 " (%" B_PRId32 "), ", countIterator->first, countIterator->second); } printf("\n"); if (!flavorInfo.info_valid) continue; printf(" addon-id %" B_PRId32 ", addon-flavor-id %" B_PRId32 ", " "addon-name \"%s\"\n", flavorInfo.info.node_info.addon, flavorInfo.info.node_info.flavor_id, flavorInfo.info.node_info.name); printf(" flavor-kinds %#08" B_PRIx64 ", flavor_flags %#08" B_PRIx32 ", internal_id %" B_PRId32 ", possible_count %" B_PRId32 ", " "in_format_count %" B_PRId32 ", out_format_count %" B_PRId32 "\n", flavorInfo.info.kinds, flavorInfo.info.flavor_flags, flavorInfo.info.internal_id, flavorInfo.info.possible_count, flavorInfo.info.in_format_count, flavorInfo.info.out_format_count); printf(" flavor-name \"%s\"\n", flavorInfo.info.name); printf(" flavor-info \"%s\"\n", flavorInfo.info.info); } printf("NodeManager: list end\n"); fDefaultManager->Dump(); } // #pragma mark - private methods status_t NodeManager::_AcquireNodeReference(media_node_id id, team_id team) { TRACE("NodeManager::_AcquireNodeReference enter: node %" B_PRId32 ", team " "%" B_PRId32 "\n", id, team); BAutolock _(this); NodeMap::iterator found = fNodeMap.find(id); if (found == fNodeMap.end()) { ERROR("NodeManager::_AcquireNodeReference: node %" B_PRId32 " not " "found\n", id); return B_ERROR; } registered_node& node = found->second; TeamCountMap::iterator teamRef = node.team_ref_count.find(team); if (teamRef == node.team_ref_count.end()) { // This is the team's first reference try { node.team_ref_count.insert(std::make_pair(team, 1)); } catch (std::bad_alloc& exception) { return B_NO_MEMORY; } } else { // Just increase its ref count teamRef->second++; } node.ref_count++; TRACE("NodeManager::_AcquireNodeReference leave: node %" B_PRId32 ", team " "%" B_PRId32 ", ref %" B_PRId32 ", team ref %" B_PRId32 "\n", id, team, node.ref_count, node.team_ref_count.find(team)->second); return B_OK; } void NodeManager::_NotifyTimeSource(registered_node& node) { team_id team = be_app->Team(); media_node timeSource; // Ensure the timesource ensure still exists if (GetCloneForID(node.timesource_id, team, &timeSource) != B_OK) return; media_node currentNode; if (GetCloneForID(node.node_id, team, ¤tNode) == B_OK) { timesource_remove_slave_node_command cmd; cmd.node = currentNode; // Notify slave node removal to owner timesource SendToPort(timeSource.port, TIMESOURCE_REMOVE_SLAVE_NODE, &cmd, sizeof(cmd)); ReleaseNode(timeSource, team); } ReleaseNode(currentNode, team); }