/* * Copyright 2002, 2003 Marcus Overhagen, Jérôme Duval. All rights reserved. * Distributed under the terms of the MIT License. */ #include "DefaultManager.h" #include #include #include #include #include #include #include #include #include #include "MediaDebug.h" #include "DormantNodeManager.h" #include "media_server.h" #include "NodeManager.h" /* no locking used in this file, we assume that the caller (NodeManager) does it. */ #define MAX_NODE_INFOS 10 #define MAX_INPUT_INFOS 10 const uint32 kMsgHeader = 'sepx'; const uint32 kMsgTypeVideoIn = 0xffffffef; const uint32 kMsgTypeVideoOut = 0xffffffee; const uint32 kMsgTypeAudioIn = 0xfffffffe; const uint32 kMsgTypeAudioOut = 0xffffffff; const char *kDefaultManagerType = "be:_default"; const char *kDefaultManagerAddon = "be:_addon_id"; const char *kDefaultManagerFlavorId = "be:_internal_id"; const char *kDefaultManagerFlavorName = "be:_flavor_name"; const char *kDefaultManagerPath = "be:_path"; const char *kDefaultManagerInput = "be:_input_id"; const char *kDefaultManagerSettingsDirectory = "Media"; const char *kDefaultManagerSettingsFile = "MDefaultManager"; DefaultManager::DefaultManager() : fMixerConnected(false), fPhysicalVideoOut(-1), fPhysicalVideoIn(-1), fPhysicalAudioOut(-1), fPhysicalAudioIn(-1), fSystemTimeSource(-1), fTimeSource(-1), fAudioMixer(-1), fPhysicalAudioOutInputID(0), fRescanThread(-1), fRescanRequested(0), fRescanLock("rescan default manager"), fRoster(NULL) { strcpy(fPhysicalAudioOutInputName, "default"); fBeginHeader[0] = 0xab00150b; fBeginHeader[1] = 0x18723462; fBeginHeader[2] = 0x00000002; fEndHeader[0] = 0x7465726d; fEndHeader[1] = 0x6d666c67; fEndHeader[2] = 0x00000002; fRoster = BMediaRoster::Roster(); if (fRoster == NULL) TRACE("DefaultManager: The roster is NULL\n"); } DefaultManager::~DefaultManager() { } // this is called by the media_server *before* any add-ons have been loaded status_t DefaultManager::LoadState() { CALLED(); status_t err = B_OK; BPath path; if ((err = find_directory(B_USER_SETTINGS_DIRECTORY, &path)) != B_OK) return err; path.Append(kDefaultManagerSettingsDirectory); path.Append(kDefaultManagerSettingsFile); BFile file(path.Path(), B_READ_ONLY); uint32 categoryCount; ssize_t size = sizeof(uint32) * 3; if (file.Read(fBeginHeader, size) < size) return B_ERROR; TRACE("0x%08lx %ld\n", fBeginHeader[0], fBeginHeader[0]); TRACE("0x%08lx %ld\n", fBeginHeader[1], fBeginHeader[1]); TRACE("0x%08lx %ld\n", fBeginHeader[2], fBeginHeader[2]); size = sizeof(uint32); if (file.Read(&categoryCount, size) < size) { fprintf(stderr, "DefaultManager::LoadState() failed to read categoryCount\n"); return B_ERROR; } TRACE("DefaultManager::LoadState() categoryCount %ld\n", categoryCount); while (categoryCount--) { BMessage settings; uint32 msg_header; uint32 default_type; if (file.Read(&msg_header, size) < size) { fprintf(stderr, "DefaultManager::LoadState() failed to read msg_header\n"); return B_ERROR; } if (file.Read(&default_type, size) < size) { fprintf(stderr, "DefaultManager::LoadState() failed to read default_type\n"); return B_ERROR; } if (settings.Unflatten(&file) == B_OK) fMsgList.AddItem(new BMessage(settings)); else fprintf(stderr, "DefaultManager::LoadState() failed to unflatten\n"); } size = sizeof(uint32) * 3; if (file.Read(fEndHeader,size) < size) { fprintf(stderr, "DefaultManager::LoadState() failed to read fEndHeader\n"); return B_ERROR; } TRACE("LoadState returns B_OK\n"); return B_OK; } status_t DefaultManager::SaveState(NodeManager *node_manager) { CALLED(); status_t err = B_OK; BPath path; BList list; if ((err = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true)) != B_OK) return err; path.Append(kDefaultManagerSettingsDirectory); if ((err = create_directory(path.Path(), 0755)) != B_OK) return err; path.Append(kDefaultManagerSettingsFile); uint32 default_types[] = {kMsgTypeVideoIn, kMsgTypeVideoOut, kMsgTypeAudioIn, kMsgTypeAudioOut}; media_node_id media_node_ids[] = {fPhysicalVideoIn, fPhysicalVideoOut, fPhysicalAudioIn, fPhysicalAudioOut}; for (uint32 i = 0; i < sizeof(default_types) / sizeof(default_types[0]); i++) { // we call the node manager to have more infos about nodes dormant_node_info info; media_node node; entry_ref ref; if (node_manager->GetCloneForID(media_node_ids[i], be_app->Team(), &node) != B_OK || node_manager->GetDormantNodeInfo(node, &info) != B_OK || node_manager->ReleaseNodeReference(media_node_ids[i], be_app->Team()) != B_OK || node_manager->GetAddOnRef(info.addon, &ref) != B_OK) { if (media_node_ids[i] != -1) { // failed to get node info thus just return return B_ERROR; } continue; } BMessage *settings = new BMessage(); settings->AddInt32(kDefaultManagerType, default_types[i]); BPath path(&ref); settings->AddInt32(kDefaultManagerAddon, info.addon); settings->AddInt32(kDefaultManagerFlavorId, info.flavor_id); settings->AddInt32(kDefaultManagerInput, default_types[i] == kMsgTypeAudioOut ? fPhysicalAudioOutInputID : 0); settings->AddString(kDefaultManagerFlavorName, info.name); settings->AddString(kDefaultManagerPath, path.Path()); list.AddItem(settings); TRACE("message %s added\n", info.name); } BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); if (file.Write(fBeginHeader, sizeof(uint32)*3) < (int32)sizeof(uint32)*3) return B_ERROR; int32 categoryCount = list.CountItems(); if (file.Write(&categoryCount, sizeof(uint32)) < (int32)sizeof(uint32)) return B_ERROR; for (int32 i = 0; i < categoryCount; i++) { BMessage *settings = (BMessage *)list.ItemAt(i); uint32 default_type; if (settings->FindInt32(kDefaultManagerType, (int32*)&default_type) < B_OK) return B_ERROR; if (file.Write(&kMsgHeader, sizeof(uint32)) < (int32)sizeof(uint32)) return B_ERROR; if (file.Write(&default_type, sizeof(uint32)) < (int32)sizeof(uint32)) return B_ERROR; if (settings->Flatten(&file) < B_OK) return B_ERROR; delete settings; } if (file.Write(fEndHeader, sizeof(uint32)*3) < (int32)sizeof(uint32)*3) return B_ERROR; return B_OK; } status_t DefaultManager::Set(media_node_id node_id, const char *input_name, int32 input_id, node_type type) { CALLED(); TRACE("DefaultManager::Set type : %i, node : %li, input : %li\n", type, node_id, input_id); switch (type) { case VIDEO_INPUT: fPhysicalVideoIn = node_id; return B_OK; case AUDIO_INPUT: fPhysicalAudioIn = node_id; return B_OK; case VIDEO_OUTPUT: fPhysicalVideoOut = node_id; return B_OK; case AUDIO_MIXER: return B_ERROR; case AUDIO_OUTPUT: fPhysicalAudioOut = node_id; fPhysicalAudioOutInputID = input_id; strcpy(fPhysicalAudioOutInputName, input_name ? input_name : ""); return B_OK; case TIME_SOURCE: return B_ERROR; // called by the media_server's ServerApp::StartSystemTimeSource() case SYSTEM_TIME_SOURCE: { ASSERT(fSystemTimeSource == -1); fSystemTimeSource = node_id; return B_OK; } default: { ERROR("DefaultManager::Set Error: called with unknown type %d\n", type); return B_ERROR; } } } status_t DefaultManager::Get(media_node_id *nodeid, char *input_name, int32 *inputid, node_type type) { CALLED(); switch (type) { case VIDEO_INPUT: // output: nodeid if (fPhysicalVideoIn == -1) return B_NAME_NOT_FOUND; *nodeid = fPhysicalVideoIn; return B_OK; case AUDIO_INPUT: // output: nodeid if (fPhysicalAudioIn == -1) return B_NAME_NOT_FOUND; *nodeid = fPhysicalAudioIn; return B_OK; case VIDEO_OUTPUT: // output: nodeid if (fPhysicalVideoOut == -1) return B_NAME_NOT_FOUND; *nodeid = fPhysicalVideoOut; return B_OK; case AUDIO_OUTPUT: // output: nodeid if (fPhysicalAudioOut == -1) return B_NAME_NOT_FOUND; *nodeid = fPhysicalAudioOut; return B_OK; case AUDIO_OUTPUT_EX: // output: nodeid, input_name, input_id if (fPhysicalAudioOut == -1) return B_NAME_NOT_FOUND; *nodeid = fPhysicalAudioOut; *inputid = fPhysicalAudioOutInputID; strcpy(input_name, fPhysicalAudioOutInputName); return B_OK; case AUDIO_MIXER: // output: nodeid if (fAudioMixer == -1) return B_NAME_NOT_FOUND; *nodeid = fAudioMixer; return B_OK; case TIME_SOURCE: if (fTimeSource != -1) *nodeid = fTimeSource; else *nodeid = fSystemTimeSource; return B_OK; case SYSTEM_TIME_SOURCE: *nodeid = fSystemTimeSource; return B_OK; default: { ERROR("DefaultManager::Get Error: called with unknown type %d\n", type); return B_ERROR; } } } // this is called by the media_server *after* the initial add-on loading // has been done status_t DefaultManager::Rescan() { BAutolock locker(fRescanLock); atomic_add(&fRescanRequested, 1); if (fRescanThread < 0) { fRescanThread = spawn_thread(rescan_thread, "rescan defaults", B_NORMAL_PRIORITY - 2, this); resume_thread(fRescanThread); } return B_OK; } int32 DefaultManager::rescan_thread(void *arg) { reinterpret_cast(arg)->_RescanThread(); return 0; } void DefaultManager::_RescanThread() { TRACE("DefaultManager::_RescanThread() enter\n"); BAutolock locker(fRescanLock); while (atomic_and(&fRescanRequested, 0) != 0) { locker.Unlock(); // We do not search for the system time source, // it should already exist ASSERT(fSystemTimeSource != -1); if (fPhysicalVideoOut == -1) { _FindPhysical(&fPhysicalVideoOut, kMsgTypeVideoOut, false, B_MEDIA_RAW_VIDEO); _FindPhysical(&fPhysicalVideoOut, kMsgTypeVideoOut, false, B_MEDIA_ENCODED_VIDEO); } if (fPhysicalVideoIn == -1) { _FindPhysical(&fPhysicalVideoIn, kMsgTypeVideoIn, true, B_MEDIA_RAW_VIDEO); _FindPhysical(&fPhysicalVideoIn, kMsgTypeVideoIn, true, B_MEDIA_ENCODED_VIDEO); } if (fPhysicalAudioOut == -1) _FindPhysical(&fPhysicalAudioOut, kMsgTypeAudioOut, false, B_MEDIA_RAW_AUDIO); if (fPhysicalAudioIn == -1) _FindPhysical(&fPhysicalAudioIn, kMsgTypeAudioIn, true, B_MEDIA_RAW_AUDIO); 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 = _ConnectMixerToOutput() == B_OK; if (!fMixerConnected) TRACE("DefaultManager: failed to connect mixer and " "soundcard\n"); } else { TRACE("DefaultManager: Did not try to connect mixer and " "soundcard\n"); } if (fMixerConnected) { add_on_server_rescan_finished_notify_command cmd; SendToAddOnServer(ADD_ON_SERVER_RESCAN_FINISHED_NOTIFY, &cmd, sizeof(cmd)); } locker.Lock(); } fRescanThread = -1; BMessage msg(MEDIA_SERVER_RESCAN_COMPLETED); be_app->PostMessage(&msg); TRACE("DefaultManager::_RescanThread() leave\n"); } void DefaultManager::_FindPhysical(volatile media_node_id *id, uint32 default_type, bool isInput, media_type type) { live_node_info info[MAX_NODE_INFOS]; media_format format; int32 count; status_t rv; BMessage *msg = NULL; BPath msgPath; dormant_node_info msgDninfo; int32 input_id; bool isAudio = (type == B_MEDIA_RAW_AUDIO) || (type == B_MEDIA_ENCODED_AUDIO); for (int32 i = 0; i < fMsgList.CountItems(); i++) { msg = (BMessage *)fMsgList.ItemAt(i); int32 msgType; if (msg->FindInt32(kDefaultManagerType, &msgType) == B_OK && ((uint32)msgType == default_type)) { const char *name = NULL; const char *path = NULL; msg->FindInt32(kDefaultManagerAddon, &msgDninfo.addon); msg->FindInt32(kDefaultManagerFlavorId, &msgDninfo.flavor_id); msg->FindInt32(kDefaultManagerInput, &input_id); msg->FindString(kDefaultManagerFlavorName, &name); msg->FindString(kDefaultManagerPath, &path); if (name) strcpy(msgDninfo.name, name); if (path) msgPath = BPath(path); break; } } format.type = type; count = MAX_NODE_INFOS; rv = fRoster->GetLiveNodes(&info[0], &count, isInput ? NULL : &format, isInput ? &format : NULL, NULL, isInput ? B_BUFFER_PRODUCER | B_PHYSICAL_INPUT : B_BUFFER_CONSUMER | B_PHYSICAL_OUTPUT); if (rv != B_OK || count < 1) { TRACE("Couldn't find physical %s %s node\n", isAudio ? "audio" : "video", isInput ? "input" : "output"); return; } for (int i = 0; i < count; i++) TRACE("info[%d].name %s\n", i, info[i].name); for (int i = 0; i < count; i++) { if (isAudio) { if (isInput) { if (0 == strcmp(info[i].name, "None In")) { // we keep the Null audio driver if none else matchs *id = info[i].node.node; continue; } // skip the Firewire audio driver if (0 == strcmp(info[i].name, "DV Input")) continue; } else { if (0 == strcmp(info[i].name, "None Out")) { // we keep the Null audio driver if none else matchs *id = info[i].node.node; if (msg) fPhysicalAudioOutInputID = input_id; continue; } // skip the Firewire audio driver if (0 == strcmp(info[i].name, "DV Output")) continue; } } if (msg) { // we have a default info msg dormant_node_info dninfo; if (fRoster->GetDormantNodeFor(info[i].node, &dninfo) != B_OK) { ERROR("Couldn't GetDormantNodeFor\n"); continue; } if (dninfo.flavor_id != msgDninfo.flavor_id || strcmp(dninfo.name, msgDninfo.name) != 0) { ERROR("Doesn't match flavor or name\n"); continue; } BPath path; if (gDormantNodeManager->FindAddOnPath(&path, dninfo.addon) != B_OK || path != msgPath) { ERROR("Doesn't match : path\n"); continue; } } TRACE("Default physical %s %s \"%s\" created!\n", isAudio ? "audio" : "video", isInput ? "input" : "output", info[i].name); *id = info[i].node.node; if (msg && isAudio && !isInput) fPhysicalAudioOutInputID = input_id; return; } } void DefaultManager::_FindTimeSource() { live_node_info info[MAX_NODE_INFOS]; media_format input; /* a physical audio output has a logical data input (DAC)*/ int32 count; status_t rv; /* First try to use the current default physical audio out */ if (fPhysicalAudioOut != -1) { media_node clone; if (fRoster->GetNodeFor(fPhysicalAudioOut, &clone) == B_OK) { if (clone.kind & B_TIME_SOURCE) { fTimeSource = clone.node; fRoster->StartTimeSource(clone, system_time() + 1000); fRoster->ReleaseNode(clone); TRACE("Default DAC timesource created!\n"); return; } fRoster->ReleaseNode(clone); } else { TRACE("Default DAC is not a timesource!\n"); } } else { TRACE("Default DAC node does not exist!\n"); } /* Now try to find another physical audio out node */ input.type = B_MEDIA_RAW_AUDIO; count = MAX_NODE_INFOS; rv = fRoster->GetLiveNodes(&info[0], &count, &input, NULL, NULL, B_TIME_SOURCE | B_PHYSICAL_OUTPUT); if (rv == B_OK && count >= 1) { for (int i = 0; i < count; i++) printf("info[%d].name %s\n", i, info[i].name); for (int i = 0; i < count; i++) { // The BeOS R5 None Out node pretend to be a physical time source, // that is pretty dumb // skip the Null audio driver if (0 == strcmp(info[i].name, "None Out")) continue; // skip the Firewire audio driver if (0 != strstr(info[i].name, "DV Output")) continue; TRACE("Default DAC timesource \"%s\" created!\n", info[i].name); fTimeSource = info[i].node.node; fRoster->StartTimeSource(info[i].node, system_time() + 1000); return; } } else { TRACE("Couldn't find DAC timesource node\n"); } /* XXX we might use other audio or video clock timesources */ } void DefaultManager::_FindAudioMixer() { live_node_info info; int32 count; status_t rv; if (fRoster == NULL) fRoster = BMediaRoster::Roster(); count = 1; rv = fRoster->GetLiveNodes(&info, &count, NULL, NULL, NULL, B_BUFFER_PRODUCER | B_BUFFER_CONSUMER | B_SYSTEM_MIXER); if (rv != B_OK || count != 1) { TRACE("Couldn't find audio mixer node\n"); return; } fAudioMixer = info.node.node; TRACE("Default audio mixer node created\n"); } status_t DefaultManager::_ConnectMixerToOutput() { media_node timesource; media_node mixer; media_node soundcard; media_input inputs[MAX_INPUT_INFOS]; media_input input; media_output output; media_input newinput; media_output newoutput; media_format format; BTimeSource * ts; bigtime_t start_at; int32 count; status_t rv; if (fRoster == NULL) fRoster = BMediaRoster::Roster(); rv = fRoster->GetNodeFor(fPhysicalAudioOut, &soundcard); if (rv != B_OK) { TRACE("DefaultManager: failed to find soundcard (physical audio " "output)\n"); return B_ERROR; } rv = fRoster->GetNodeFor(fAudioMixer, &mixer); if (rv != B_OK) { fRoster->ReleaseNode(soundcard); TRACE("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 = fRoster->GetFreeOutputsFor(mixer, &output, 1, &count, B_MEDIA_RAW_AUDIO); if (rv != B_OK || count != 1) { TRACE("DefaultManager: can't find free mixer output\n"); rv = B_ERROR; goto finish; } rv = fRoster->GetFreeInputsFor(soundcard, inputs, MAX_INPUT_INFOS, &count, B_MEDIA_RAW_AUDIO); if (rv != B_OK || count < 1) { TRACE("DefaultManager: can't find free soundcard inputs\n"); rv = B_ERROR; goto finish; } for (int32 i = 0; i < count; i++) { input = inputs[i]; if (input.destination.id == fPhysicalAudioOutInputID) break; } for (int i = 0; i < 6; i++) { switch (i) { case 0: TRACE("DefaultManager: Trying connect in native format (1)\n"); if (fRoster->GetFormatFor(input, &format) != B_OK) { ERROR("DefaultManager: GetFormatFor failed\n"); continue; } // XXX BeOS R5 multiaudio node bug workaround if (format.u.raw_audio.channel_count == 1) { TRACE("##### WARNING! DefaultManager: ignored mono format\n"); continue; } break; case 1: TRACE("DefaultManager: Trying connect in format 1\n"); format.Clear(); format.type = B_MEDIA_RAW_AUDIO; format.u.raw_audio.frame_rate = 44100; format.u.raw_audio.channel_count = 2; format.u.raw_audio.format = 0x2; break; case 2: TRACE("DefaultManager: Trying connect in format 2\n"); format.Clear(); format.type = B_MEDIA_RAW_AUDIO; format.u.raw_audio.frame_rate = 48000; format.u.raw_audio.channel_count = 2; format.u.raw_audio.format = 0x2; break; case 3: TRACE("DefaultManager: Trying connect in format 3\n"); format.Clear(); format.type = B_MEDIA_RAW_AUDIO; break; case 4: // BeOS R5 multiaudio node bug workaround TRACE("DefaultManager: Trying connect in native format (2)\n"); if (fRoster->GetFormatFor(input, &format) != B_OK) { ERROR("DefaultManager: GetFormatFor failed\n"); continue; } break; case 5: TRACE("DefaultManager: Trying connect in format 4\n"); format.Clear(); break; } rv = fRoster->Connect(output.source, input.destination, &format, &newoutput, &newinput); if (rv == B_OK) break; } if (rv != B_OK) { ERROR("DefaultManager: connect failed\n"); goto finish; } fRoster->SetRunModeNode(mixer, BMediaNode::B_INCREASE_LATENCY); fRoster->SetRunModeNode(soundcard, BMediaNode::B_RECORDING); fRoster->GetTimeSource(×ource); fRoster->SetTimeSourceFor(mixer.node, timesource.node); fRoster->SetTimeSourceFor(soundcard.node, timesource.node); fRoster->PrerollNode(mixer); fRoster->PrerollNode(soundcard); ts = fRoster->MakeTimeSourceFor(mixer); start_at = ts->Now() + 50000; fRoster->StartNode(mixer, start_at); fRoster->StartNode(soundcard, start_at); ts->Release(); finish: fRoster->ReleaseNode(mixer); fRoster->ReleaseNode(soundcard); fRoster->ReleaseNode(timesource); return rv; } void DefaultManager::Dump() { } void DefaultManager::CleanupTeam(team_id team) { }