diff --git a/headers/os/media/MediaAddOn.h b/headers/os/media/MediaAddOn.h index 85b58747ec..10d6118ae9 100644 --- a/headers/os/media/MediaAddOn.h +++ b/headers/os/media/MediaAddOn.h @@ -109,7 +109,7 @@ virtual status_t GetConfigurationFor( BMessage * into_message); virtual bool WantsAutoStart(); virtual status_t AutoStart( - int in_count, + int in_index, BMediaNode ** out_node, int32 * out_internal_id, bool * out_has_more); diff --git a/headers/private/media/DataExchange.h b/headers/private/media/DataExchange.h index 84f2aaa6a3..63f1d4659f 100644 --- a/headers/private/media/DataExchange.h +++ b/headers/private/media/DataExchange.h @@ -111,6 +111,7 @@ enum { NODE_SET_TIMESOURCE, NODE_REQUEST_COMPLETED, NODE_FINAL_RELEASE, + NODE_PUBLISH_CONNECTIONS, NODE_MESSAGE_END, CONSUMER_MESSAGE_START = 0x300, diff --git a/headers/private/media/MediaMisc.h b/headers/private/media/MediaMisc.h index 8d2f875851..375d728ebf 100644 --- a/headers/private/media/MediaMisc.h +++ b/headers/private/media/MediaMisc.h @@ -21,7 +21,11 @@ #define SYSTEM_TIMESOURCE_CONTROL_PORT -666 #define IS_SYSTEM_TIMESOURCE(_node) ((_node).node > 0 && (_node).port == SYSTEM_TIMESOURCE_CONTROL_PORT) -#define NODE_KIND_NO_REFCOUNTING 0x80000000 +#define NODE_KIND_USER_MASK 0x00000000FFFFFFFFLL +#define NODE_KIND_COMPARE_MASK 0x000000007FFFFFFFLL +#define NODE_KIND_NO_REFCOUNTING 0x0000000080000000LL +#define NODE_KIND_SHADOW_TIMESOURCE 0x0000000100000000LL +#define NODE_KIND_SYSTEM_TIMESOURCE 0x0000000200000000LL #define ROUND_UP_TO_PAGE(size) (((size) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1)) diff --git a/src/kits/media/MediaRoster.cpp b/src/kits/media/MediaRoster.cpp index 6b5a46bbba..dd319dc4c7 100644 --- a/src/kits/media/MediaRoster.cpp +++ b/src/kits/media/MediaRoster.cpp @@ -1312,7 +1312,7 @@ BMediaRoster::GetFreeOutputsFor(const media_node & node, if (buf_num_outputs == 0) break; } - + MediaRosterEx(this)->PublishOutputs(node, &list); return B_OK; } @@ -1550,21 +1550,38 @@ BMediaRosterEx::RegisterNode(BMediaNode * node, media_addon_id addonid, int32 fl ts->FinishCreate(); } + // XXX XXX XXX + // Calling GetAllOutputs and GetAllInputs here, + // followed by PublishOutputs and PublishInputs, + // does not work, it is too early, and in some + // cases it will deadlock the node control thread. + + // Posting a message to the roster thread here + // is also to early, the multi_audio add-on will + // no list any inputs + // XXX XXX XXX + /* + BMessage msg(NODE_PUBLISH_CONNECTIONS); + media_node tempnode; + tempnode = node->Node(); + msg.AddData("node", B_RAW_TYPE, &tempnode, sizeof(tempnode)); + PostMessage(&msg); +*/ +/* // register existing inputs and outputs with the // media_server, this allows GetLiveNodes() to work // with created, but unconnected nodes. if (node->Kinds() & B_BUFFER_PRODUCER) { - Stack stack; - if (B_OK == GetAllOutputs(node->Node(), &stack)) - PublishOutputs(node->Node(), &stack); + List list; + if (B_OK == GetAllOutputs(node->Node(), &list)) + PublishOutputs(node->Node(), &list); } else if (node->Kinds() & B_BUFFER_CONSUMER) { - Stack stack; - if (B_OK == GetAllInputs(node->Node(), &stack)) - PublishInputs(node->Node(), &stack); + List list; + if (B_OK == GetAllInputs(node->Node(), &list)) + PublishInputs(node->Node(), &list); } */ - BPrivate::media::notifications::NodesCreated(&reply.nodeid, 1); /* TRACE("BMediaRoster::RegisterNode: registered node name '%s', id %ld, addon %ld, flavor %ld\n", node->Name(), node->ID(), addon_id, addon_flavor_id); @@ -2441,6 +2458,33 @@ BMediaRoster::MessageReceived(BMessage * message) message->SendReply(&pong, static_cast(NULL), 2000000); return; } + + case NODE_PUBLISH_CONNECTIONS: + { + const void *data; + ssize_t numBytes; + media_node node; + + printf("NODE_PUBLISH_CONNECTIONS enter\n"); + + message->FindData("node", B_RAW_TYPE, &data, &numBytes); + node = *(const media_node *)data; + + // register existing inputs and outputs with the + // media_server, this allows GetLiveNodes() to work + // with created, but unconnected nodes. + if (node.kind & B_BUFFER_PRODUCER) { + List outputlist; + if (B_OK == MediaRosterEx(this)->GetAllOutputs(node, &outputlist)) + MediaRosterEx(this)->PublishOutputs(node, &outputlist); + } else if (node.kind & B_BUFFER_CONSUMER) { + List inputlist; + if (B_OK == MediaRosterEx(this)->GetAllInputs(node, &inputlist)) + MediaRosterEx(this)->PublishInputs(node, &inputlist); + } + printf("NODE_PUBLISH_CONNECTIONS leave\n"); + return; + } case NODE_FINAL_RELEASE: { diff --git a/src/kits/media/TimeSourceObject.h b/src/kits/media/TimeSourceObject.h index fdb98154c6..2407950ef9 100644 --- a/src/kits/media/TimeSourceObject.h +++ b/src/kits/media/TimeSourceObject.h @@ -10,10 +10,7 @@ #define _TIME_SOURCE_OBJECT_H_ #include - -#define NODE_KIND_USER_MASK 0x00000000FFFFFFFFLL -#define NODE_KIND_SHADOW_TIMESOURCE 0x0000000100000000LL -#define NODE_KIND_SYSTEM_TIMESOURCE 0x0000000200000000LL +#include "MediaMisc.h" namespace BPrivate { namespace media { diff --git a/src/servers/media/AppManager.cpp b/src/servers/media/AppManager.cpp index 5c43c3c5f3..979aa12064 100644 --- a/src/servers/media/AppManager.cpp +++ b/src/servers/media/AppManager.cpp @@ -105,6 +105,16 @@ status_t AppManager::UnregisterTeam(team_id team) return is_removed ? B_OK : B_ERROR; } +status_t +AppManager::SendMessage(team_id team, BMessage *msg) +{ + BAutolock lock(fLocker); + App *app; + if (!fAppMap->Get(team, &app)) + return B_ERROR; + return app->messenger.SendMessage(msg); +} + void AppManager::RestartAddonServer() { static bigtime_t restart_period = 0; diff --git a/src/servers/media/AppManager.h b/src/servers/media/AppManager.h index fed4d23ed3..f3b3edc462 100644 --- a/src/servers/media/AppManager.h +++ b/src/servers/media/AppManager.h @@ -19,6 +19,8 @@ public: void TerminateAddonServer(); team_id AddonServer(); + status_t SendMessage(team_id team, BMessage *msg); + void Dump(); private: diff --git a/src/servers/media/DefaultManager.cpp b/src/servers/media/DefaultManager.cpp index 9e08eaf5e0..0000e9aa90 100644 --- a/src/servers/media/DefaultManager.cpp +++ b/src/servers/media/DefaultManager.cpp @@ -6,15 +6,18 @@ #include #include #include -#include #include "DefaultManager.h" +#include "NodeManager.h" #include "debug.h" +extern NodeManager *gNodeManager; + /* no locking used in this file, we assume that the caller (NodeManager) does it. */ DefaultManager::DefaultManager() - : fPhysicalVideoOut(-1), + : fMixerConnected(false), + fPhysicalVideoOut(-1), fPhysicalVideoIn(-1), fPhysicalAudioOut(-1), fPhysicalAudioIn(-1), @@ -153,11 +156,15 @@ void DefaultManager::RescanThread() { printf("DefaultManager::RescanThread() enter\n"); - + // We do not search for the system time source, // it should already exist ASSERT(fSystemTimeSource != -1); - + +for (int i = 0; i < 10; i++) { // XXX ugly workaround + gNodeManager->UpdateNodeConnections(); // XXX ugly workaround + snooze(1000000); // XXX ugly workaround + if (fPhysicalVideoOut == -1) FindPhysicalVideoOut(); if (fPhysicalVideoIn == -1) @@ -168,144 +175,218 @@ DefaultManager::RescanThread() FindPhysicalAudioIn(); if (fAudioMixer == -1) FindAudioMixer(); +} + // The normal time source is searched for after the // Physical Audio Out has been created. if (fTimeSource == -1) FindTimeSource(); + // Connect the mixer and physical audio out (soundcard) + if (!fMixerConnected && fAudioMixer != -1 && fPhysicalAudioOut != -1) { + fMixerConnected = B_OK == ConnectMixerToOutput(); + if (!fMixerConnected) + FATAL("DefaultManager: failed to connect mixer and soundcard\n"); + } + printf("DefaultManager::RescanThread() leave\n"); } void DefaultManager::FindPhysicalVideoOut() { - dormant_node_info info; + live_node_info info; media_format input; /* a physical video output has a logical data input */ - media_node node; int32 count; status_t rv; memset(&input, 0, sizeof(input)); input.type = B_MEDIA_RAW_VIDEO; count = 1; - rv = BMediaRoster::Roster()->GetDormantNodes(&info, &count, &input, NULL, NULL, B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT); + rv = BMediaRoster::Roster()->GetLiveNodes(&info, &count, &input, NULL, NULL, B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT); if (rv != B_OK || count != 1) { printf("Couldn't find physical video output node\n"); return; } - rv = BMediaRoster::Roster()->InstantiateDormantNode(info, &node, B_FLAVOR_IS_GLOBAL); - if (rv != B_OK) { - printf("Couldn't instantiate physical video output node\n"); - } else { - printf("Default physical video output created!\n"); - fPhysicalVideoOut = node.node; - } + printf("Default physical video output created!\n"); + fPhysicalVideoOut = info.node.node; } void DefaultManager::FindPhysicalVideoIn() { - dormant_node_info info; + live_node_info info; media_format output; /* a physical video input has a logical data output */ - media_node node; int32 count; status_t rv; memset(&output, 0, sizeof(output)); output.type = B_MEDIA_RAW_VIDEO; count = 1; - rv = BMediaRoster::Roster()->GetDormantNodes(&info, &count, NULL, &output, NULL, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); + rv = BMediaRoster::Roster()->GetLiveNodes(&info, &count, NULL, &output, NULL, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); if (rv != B_OK || count != 1) { printf("Couldn't find physical video input node\n"); return; } - rv = BMediaRoster::Roster()->InstantiateDormantNode(info, &node, B_FLAVOR_IS_GLOBAL); - if (rv != B_OK) { - printf("Couldn't instantiate physical video input node\n"); - } else { - printf("Default physical video input created!\n"); - fPhysicalVideoIn = node.node; - } + printf("Default physical video input created!\n"); + fPhysicalVideoIn = info.node.node; } void DefaultManager::FindPhysicalAudioOut() { - dormant_node_info info; + live_node_info info[2]; media_format input; /* a physical audio output has a logical data input */ - media_node node; int32 count; status_t rv; memset(&input, 0, sizeof(input)); input.type = B_MEDIA_RAW_AUDIO; - count = 1; - rv = BMediaRoster::Roster()->GetDormantNodes(&info, &count, &input, NULL, NULL, B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT); - if (rv != B_OK || count != 1) { + count = 2; + rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count, &input, NULL, NULL, B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT); + if (rv != B_OK || count < 1) { printf("Couldn't find physical audio output node\n"); return; } - rv = BMediaRoster::Roster()->InstantiateDormantNode(info, &node, B_FLAVOR_IS_GLOBAL); - if (rv != B_OK) { - printf("Couldn't instantiate physical audio output node\n"); - } else { + for (int i = 0; i < count; i++) + printf("info[%d].name %s\n", i, info[i].name); + + for (int i = 0; i < count; i++) { + if (0 == strcmp(info[i].name, "None Out")) // skip the Null audio driver + continue; printf("Default physical audio output created!\n"); - fPhysicalAudioOut = node.node; + fPhysicalAudioOut = info[i].node.node; + return; } } void DefaultManager::FindPhysicalAudioIn() { - dormant_node_info info; + live_node_info info[2]; media_format output; /* a physical audio input has a logical data output */ - media_node node; int32 count; status_t rv; memset(&output, 0, sizeof(output)); output.type = B_MEDIA_RAW_AUDIO; - count = 1; - rv = BMediaRoster::Roster()->GetDormantNodes(&info, &count, NULL, &output, NULL, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); - if (rv != B_OK || count != 1) { + count = 2; + rv = BMediaRoster::Roster()->GetLiveNodes(&info[0], &count, NULL, &output, NULL, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); + if (rv != B_OK || count < 1) { printf("Couldn't find physical audio input node\n"); return; } - rv = BMediaRoster::Roster()->InstantiateDormantNode(info, &node, B_FLAVOR_IS_GLOBAL); - if (rv != B_OK) { - printf("Couldn't instantiate physical audio input node\n"); - } else { + for (int i = 0; i < count; i++) + printf("info[%d].name %s\n", i, info[i].name); + + for (int i = 0; i < count; i++) { + if (0 == strcmp(info[i].name, "None In")) // skip the Null audio driver + continue; printf("Default physical audio input created!\n"); - fPhysicalAudioIn = node.node; + fPhysicalAudioIn = info[i].node.node; + return; } } void DefaultManager::FindTimeSource() { + live_node_info info; + media_format input; /* a physical audio output has a logical data input (DAC)*/ + int32 count; + status_t rv; + + /* We try to use the default physical audio out node, + * as it most likely is a timesource. + * XXX if that fails, we might use other audio or video clock timesources + */ + + memset(&input, 0, sizeof(input)); + input.type = B_MEDIA_RAW_AUDIO; + count = 1; + rv = BMediaRoster::Roster()->GetLiveNodes(&info, &count, &input, NULL, NULL, B_TIME_SOURCE | B_PHYSICAL_OUTPUT); + if (rv != B_OK || count != 1) { + printf("Couldn't find DAC timesource node\n"); + return; + } + fTimeSource = info.node.node; + printf("Default DAC timesource created!\n"); } void DefaultManager::FindAudioMixer() { - dormant_node_info info; - media_node node; + live_node_info info; int32 count; status_t rv; count = 1; - rv = BMediaRoster::Roster()->GetDormantNodes(&info, &count, NULL, NULL, NULL, B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER); + rv = BMediaRoster::Roster()->GetLiveNodes(&info, &count, NULL, NULL, NULL, B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER); if (rv != B_OK || count != 1) { printf("Couldn't find audio mixer node\n"); return; } - rv = BMediaRoster::Roster()->InstantiateDormantNode(info, &node, B_FLAVOR_IS_GLOBAL); + fAudioMixer = info.node.node; + printf("Default audio mixer node created\n"); +} + +status_t +DefaultManager::ConnectMixerToOutput() +{ + BMediaRoster *roster; + media_node mixer; + media_node soundcard; + media_input input; + media_output output; + media_format format; + int32 count; + status_t rv; + + roster = BMediaRoster::Roster(); + + // XXX this connects to *any* physical output, but not it's default logical input + rv = roster->GetNodeFor(fPhysicalAudioOut, &soundcard); if (rv != B_OK) { - printf("Couldn't instantiate audio mixer node\n"); - } else { - printf("Default audio mixer created!\n"); - fAudioMixer = node.node; + printf("DefaultManager: failed to find soundcard (physical audio output)\n"); + return B_ERROR; } + + rv = roster->GetNodeFor(fAudioMixer, &mixer); + if (rv != B_OK) { + roster->ReleaseNode(soundcard); + printf("DefaultManager: failed to find mixer\n"); + return B_ERROR; + } + + // we now have the mixer and soundcard nodes, + // find a free input/output and connect them + + rv = roster->GetFreeOutputsFor(mixer, &output, 1, &count, B_MEDIA_RAW_AUDIO); + if (rv != B_OK || count != 1) { + printf("DefaultManager: can't find free mixer output\n"); + rv = B_ERROR; + goto finish; + } + + rv = roster->GetFreeInputsFor(soundcard, &input, 1, &count, B_MEDIA_RAW_AUDIO); + if (rv != B_OK || count != 1) { + printf("DefaultManager: can't find free soundcard input\n"); + rv = B_ERROR; + goto finish; + } + + memset(&format, 0, sizeof(format)); + format.type = B_MEDIA_RAW_AUDIO; + + rv = roster->Connect(output.source, input.destination, &format, &output, &input); + if (rv != B_OK) { + printf("DefaultManager: connect failed\n"); + } + +finish: + roster->ReleaseNode(mixer); + roster->ReleaseNode(soundcard); + return rv; } void @@ -317,3 +398,4 @@ void DefaultManager::CleanupTeam(team_id team) { } + diff --git a/src/servers/media/DefaultManager.h b/src/servers/media/DefaultManager.h index db809de478..388abbd4ed 100644 --- a/src/servers/media/DefaultManager.h +++ b/src/servers/media/DefaultManager.h @@ -35,14 +35,17 @@ private: void FindAudioMixer(); void FindTimeSource(); + status_t ConnectMixerToOutput(); + private: - media_node_id fPhysicalVideoOut; - media_node_id fPhysicalVideoIn; - media_node_id fPhysicalAudioOut; - media_node_id fPhysicalAudioIn; - media_node_id fSystemTimeSource; - media_node_id fTimeSource; - media_node_id fAudioMixer; - int32 fPhysicalAudioOutInputID; + volatile bool fMixerConnected; + volatile media_node_id fPhysicalVideoOut; + volatile media_node_id fPhysicalVideoIn; + volatile media_node_id fPhysicalAudioOut; + volatile media_node_id fPhysicalAudioIn; + volatile media_node_id fSystemTimeSource; + volatile media_node_id fTimeSource; + volatile media_node_id fAudioMixer; + volatile int32 fPhysicalAudioOutInputID; char fPhysicalAudioOutInputName[B_MEDIA_NAME_LENGTH]; }; diff --git a/src/servers/media/NodeManager.cpp b/src/servers/media/NodeManager.cpp index 8d8673e39d..18192364c8 100644 --- a/src/servers/media/NodeManager.cpp +++ b/src/servers/media/NodeManager.cpp @@ -43,6 +43,7 @@ static char __copyright[] = "Copyright (c) 2002, 2003 Marcus Overhagen port); - ASSERT(node.kind == rn->kinds); + 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); @@ -431,7 +432,7 @@ NodeManager::GetLiveNodeInfo(live_node_info *live_info, const media_node &node) for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { if (rn->nodeid == node.node) { ASSERT(node.port == rn->port); - ASSERT(node.kind == rn->kinds); + 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); @@ -468,6 +469,9 @@ NodeManager::GetLiveNodes(Stack *livenodes, int32 maxcount, cons BAutolock lock(fLocker); registered_node *rn; int namelen; + + TRACE("NodeManager::GetLiveNodes: maxcount %d, in-format %p, out-format %p, name %s, require_kinds 0x%x\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) { @@ -539,6 +543,28 @@ NodeManager::GetLiveNodes(BMessage *msg) return B_OK; } +void +NodeManager::UpdateNodeConnections() +{ + // XXX this is only a workaround because the multi_audio + // XXX addon does not publish inputs right after creation :( + BAutolock lock(fLocker); + + registered_node *rn; + for (fRegisteredNodeMap->Rewind(); fRegisteredNodeMap->GetNext(&rn); ) { + + BMessage msg(NODE_PUBLISH_CONNECTIONS); + media_node tempnode; + tempnode.node = rn->nodeid; + tempnode.port = rn->port; + tempnode.kind = rn->kinds; + msg.AddData("node", B_RAW_TYPE, &tempnode, sizeof(tempnode)); + + gAppManager->SendMessage(rn->team, &msg); + } +} + + /********************************************************************** * Registration of BMediaAddOns **********************************************************************/ @@ -964,11 +990,15 @@ NodeManager::Dump() 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_input: none\n"); } printf("NodeManager: list end\n"); printf("\n"); diff --git a/src/servers/media/NodeManager.h b/src/servers/media/NodeManager.h index e0f7b5c42f..caaa6c0299 100644 --- a/src/servers/media/NodeManager.h +++ b/src/servers/media/NodeManager.h @@ -105,6 +105,8 @@ public: void CleanupTeam(team_id team); + void UpdateNodeConnections(); + private: media_addon_id fNextAddOnID; media_node_id fNextNodeID; diff --git a/src/servers/media_addon/main.cpp b/src/servers/media_addon/main.cpp index fe453a97ee..ec3a307f0e 100644 --- a/src/servers/media_addon/main.cpp +++ b/src/servers/media_addon/main.cpp @@ -55,6 +55,17 @@ static char __copyright[] = "Copyright (c) 2002, 2003 Marcus Overhagen active_flavors; + + BMediaAddOn *addon; // if != NULL, need to call _DormantNodeManager->PutAddon(id) +}; + class MediaAddonServer : BApplication { public: @@ -68,11 +79,16 @@ public: void AddOnRemoved(ino_t file_node); void HandleMessage(int32 code, const void *data, size_t size); static int32 controlthread(void *arg); + + void PutAddonIfPossible(AddOnInfo *info); + void InstantiatePhysialInputsAndOutputs(AddOnInfo *info); + void InstantiateAutostartFlavors(AddOnInfo *info); + void DestroyInstantiatedFlavors(AddOnInfo *info); void ScanAddOnFlavors(BMediaAddOn *addon); Map *filemap; - Map *flavorcountmap; + Map *infomap; BMediaRoster *mediaroster; ino_t DirNodeSystem; @@ -88,9 +104,9 @@ MediaAddonServer::MediaAddonServer(const char *sig) : { mediaroster = BMediaRoster::Roster(); filemap = new Map; - flavorcountmap = new Map; - control_port = create_port(64,"media_addon_server port"); - control_thread = spawn_thread(controlthread,"media_addon_server control",12,this); + infomap = new Map; + control_port = create_port(64, "media_addon_server port"); + control_thread = spawn_thread(controlthread, "media_addon_server control", 12, this); resume_thread(control_thread); } @@ -106,7 +122,7 @@ MediaAddonServer::~MediaAddonServer() _DormantNodeManager->UnregisterAddon(*id); delete filemap; - delete flavorcountmap; + delete infomap; } void @@ -160,6 +176,10 @@ MediaAddonServer::controlthread(void *arg) void MediaAddonServer::ReadyToRun() { + // the control thread is already running at this point, + // so we can talk to the media server and also receive + // commands for instantiation + // register with media_server server_register_addonserver_request request; server_register_addonserver_reply reply; @@ -172,7 +192,13 @@ MediaAddonServer::ReadyToRun() return; } - fStartup = true; + ASSERT(fStartup == true); + + // During startup, first all add-ons are loaded, then all + // nodes (flavors) representing physical inputs and outputs + // are instantiated. Next, all add-ons that need autostart + // will be autostarted. Finally, add-ons that don't have + // any active nodes (flavors) will be unloaded. // load dormant media nodes node_ref nref; @@ -191,7 +217,21 @@ MediaAddonServer::ReadyToRun() WatchDir(&e2); fStartup = false; + + AddOnInfo *info; + + infomap->Rewind(); + while (infomap->GetNext(&info)) + InstantiatePhysialInputsAndOutputs(info); + infomap->Rewind(); + while (infomap->GetNext(&info)) + InstantiateAutostartFlavors(info); + + infomap->Rewind(); + while (infomap->GetNext(&info)) + PutAddonIfPossible(info); + server_rescan_defaults_command cmd; SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); } @@ -199,7 +239,7 @@ MediaAddonServer::ReadyToRun() void MediaAddonServer::ScanAddOnFlavors(BMediaAddOn *addon) { - int32 *flavorcount; + AddOnInfo *info; int32 oldflavorcount; int32 newflavorcount; media_addon_id addon_id; @@ -223,11 +263,11 @@ MediaAddonServer::ScanAddOnFlavors(BMediaAddOn *addon) addon_id = addon->AddonID(); // update the cached flavor count, get oldflavorcount and newflavorcount - b = flavorcountmap->Get(addon->AddonID(), &flavorcount); + b = infomap->Get(addon_id, &info); ASSERT(b); - oldflavorcount = *flavorcount; + oldflavorcount = info->flavor_count; newflavorcount = addon->CountFlavors(); - *flavorcount = newflavorcount; + info->flavor_count = newflavorcount; TRACE("%ld old flavors, %ld new flavors\n", oldflavorcount, newflavorcount); @@ -296,6 +336,8 @@ MediaAddonServer::AddOnAdded(const char *path, ino_t file_node) return; } + TRACE("MediaAddonServer::AddOnAdded: loading addon %d now...\n", id); + addon = _DormantNodeManager->GetAddon(id); if (addon == NULL) { FATAL("MediaAddonServer::AddOnAdded: failed to get add-on %s\n", path); @@ -305,73 +347,142 @@ MediaAddonServer::AddOnAdded(const char *path, ino_t file_node) TRACE("MediaAddonServer::AddOnAdded: loading finished, id %ld\n", id); + // put file's inode and addon's id into map filemap->Insert(file_node, id); - flavorcountmap->Insert(id, 0); - - ScanAddOnFlavors(addon); - if (addon->WantsAutoStart()) + // also create AddOnInfo struct and get a pointer so + // we can modify it + AddOnInfo tempinfo; + infomap->Insert(id, tempinfo); + AddOnInfo *info; + infomap->Get(id, &info); + + // setup + info->id = id; + info->wants_autostart = false; // temporary default + info->flavor_count = 0; + info->addon = addon; + + // scan the flavors + ScanAddOnFlavors(addon); + + // need to call BMediaNode::WantsAutoStart() + // after the flavors have been scanned + info->wants_autostart = addon->WantsAutoStart(); + + if (info->wants_autostart) FATAL("#### add-on %ld WantsAutoStart!\n", id); -/* - * the mixer (which we can't load because of unresolved symbols) - * is the only node that uses autostart (which is not implemented yet) - */ - -/* -loading BMediaAddOn from /boot/beos/system/add-ons/media/mixer.media_addon -adding media add-on -1 -1 flavors: -flavor 0: - internal_id = 0 - name = Be Audio Mixer - info = The system-wide sound mixer of the future. - flavor_flags = 0x0 - kinds = 0x40003 B_BUFFER_PRODUCER B_BUFFER_CONSUMER B_SYSTEM_MIXER - in_format_count = 1 - out_format_count = 1 -#### WantsAutoStart! -*/ - -/* - if (addon->WantsAutoStart()) { - for (int32 index = 0; ;index++) { - BMediaNode *outNode; - int32 outInternalID; - bool outHasMore; - rv = addon->AutoStart(index, &outNode, &outInternalID, &outHasMore); - if (rv == B_OK) { - printf("started node %ld\n",index); - rv = mediaroster->RegisterNode(outNode); - if (rv != B_OK) - printf("failed to register node %ld\n",index); - if (!outHasMore) -// break; - return; - } else if (rv == B_MEDIA_ADDON_FAILED && outHasMore) { - continue; - } else { - break; - } - } - return; - } -*/ + // During startup, first all add-ons are loaded, then all + // nodes (flavors) representing physical inputs and outputs + // are instantiated. Next, all add-ons that need autostart + // will be autostarted. Finally, add-ons that don't have + // any active nodes (flavors) will be unloaded. + // After startup is done, we simply do it for each new + // loaded add-on, too. if (!fStartup) { + InstantiatePhysialInputsAndOutputs(info); + InstantiateAutostartFlavors(info); + PutAddonIfPossible(info); + + // since something might have changed server_rescan_defaults_command cmd; - SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); + SendToServer(SERVER_RESCAN_DEFAULTS, &cmd, sizeof(cmd)); } - _DormantNodeManager->PutAddon(id); + // we do not call _DormantNodeManager->PutAddon(id) + // since it is done by PutAddonIfPossible() } +void +MediaAddonServer::DestroyInstantiatedFlavors(AddOnInfo *info) +{ + printf("MediaAddonServer::DestroyInstantiatedFlavors"); +} + +void +MediaAddonServer::PutAddonIfPossible(AddOnInfo *info) +{ + if (info->addon && info->active_flavors.IsEmpty()) { + _DormantNodeManager->PutAddon(info->id); + info->addon = NULL; + } +} + +void +MediaAddonServer::InstantiatePhysialInputsAndOutputs(AddOnInfo *info) +{ + int count = info->addon->CountFlavors(); + for (int i = 0; i < count; i++) { + const flavor_info *flavorinfo; + if (B_OK != info->addon->GetFlavorAt(i, &flavorinfo)) { + FATAL("MediaAddonServer::InstantiatePhysialInputsAndOutputs GetFlavorAt failed for index %d!\n", i); + continue; + } + if (flavorinfo->kinds & (B_PHYSICAL_INPUT | B_PHYSICAL_OUTPUT)) { + media_node node; + status_t rv; + + dormant_node_info dni; + dni.addon = info->id; + dni.flavor_id = flavorinfo->internal_id; + strcpy(dni.name, flavorinfo->name); + + printf("MediaAddonServer::InstantiatePhysialInputsAndOutputs: \"%s\" is a physical input/output\n", flavorinfo->name); + rv = mediaroster->InstantiateDormantNode(dni, &node); + if (rv != B_OK) { + printf("Couldn't instantiate node\n"); + } else { + printf("Node created!\n"); + info->active_flavors.Insert(node); + } + } + } +} + +void +MediaAddonServer::InstantiateAutostartFlavors(AddOnInfo *info) +{ + if (!info->wants_autostart) + return; + + for (int32 index = 0; ;index++) { + BMediaNode *outNode; + int32 outInternalID; + bool outHasMore; + status_t rv; + printf("trying autostart of node %ld, index %ld\n", info->id, index); + rv = info->addon->AutoStart(index, &outNode, &outInternalID, &outHasMore); + if (rv == B_OK) { + printf("started node\n",index); + + // XXX IncrementAddonFlavorInstancesCount + + rv = MediaRosterEx(mediaroster)->RegisterNode(outNode, info->id, outInternalID); + if (rv != B_OK) { + printf("failed to register node %ld\n",index); + // XXX DecrementAddonFlavorInstancesCount + } + + info->active_flavors.Insert(outNode->Node()); + + if (!outHasMore) + return; + } else if (rv == B_MEDIA_ADDON_FAILED && outHasMore) { + continue; + } else { + break; + } + } +} void MediaAddonServer::AddOnRemoved(ino_t file_node) { media_addon_id *tempid; media_addon_id id; + AddOnInfo *info; int32 *tempflavorcount; int32 oldflavorcount; // XXX locking? @@ -383,14 +494,22 @@ MediaAddonServer::AddOnRemoved(ino_t file_node) id = *tempid; // tempid pointer is invalid after Removing() it from the map filemap->Remove(file_node); - if (!flavorcountmap->Get(id, &tempflavorcount)) { - FATAL("MediaAddonServer::AddOnRemoved: couldn't get flavor count for add-on %ld\n", id); + if (!infomap->Get(id, &info)) { + FATAL("MediaAddonServer::AddOnRemoved: couldn't get addon info for add-on %ld\n", id); oldflavorcount = 1000; } else { - oldflavorcount = *tempflavorcount; //same reason as above - } - flavorcountmap->Remove(id); + oldflavorcount = info->flavor_count; //same reason as above + DestroyInstantiatedFlavors(info); + PutAddonIfPossible(info); + + if (info->addon) { + FATAL("MediaAddonServer::AddOnRemoved: couldn't unload addon %ld since flavors are in use\n", id); + } + } + + + infomap->Remove(id); _DormantNodeManager->UnregisterAddon(id); BPrivate::media::notifications::FlavorsChanged(id, 0, oldflavorcount);