haiku/src/servers/media/NodeManager.cpp

1409 lines
38 KiB
C++

/*
* Copyright (c) 2015 Dario Casalinuovo
* Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
*
* 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 <Application.h>
#include <Autolock.h>
#include <Entry.h>
#include <MediaAddOn.h>
#include <MediaDefs.h>
#include <Message.h>
#include <Messenger.h>
#include <OS.h>
#include <Path.h>
#include <MediaDebug.h>
#include <MediaMisc.h>
#include <Notifications.h>
#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,
&currentNode) == 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);
}