void BMediaEventLooper::Run() { CALLED(); if (fControlThread != -1) return; // thread already running // until now, the run state is B_UNREGISTERED, but we need to start in B_STOPPED state. SetRunState(B_STOPPED); char threadName[32]; sprintf(threadName, "%.20s control", Name()); fControlThread = spawn_thread(_ControlThreadStart, threadName, fCurrentPriority, this); resume_thread(fControlThread); // get latency information fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); }
/* virtual */ void BMediaEventLooper::SetRunMode(run_mode mode) { CALLED(); // The SetRunMode() hook function is called when someone requests that your node's run mode be changed. // bump or reduce priority when switching from/to offline run mode int32 priority; priority = (mode == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; if (priority != fCurrentPriority) { fCurrentPriority = priority; if (fControlThread > 0) { set_thread_priority(fControlThread, fCurrentPriority); fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n", fSchedulingLatency); } } BMediaNode::SetRunMode(mode); }
status_t BMediaEventLooper::SetPriority(int32 priority) { CALLED(); // clamp to a valid value if (priority < 5) priority = 5; if (priority > 120) priority = 120; fSetPriority = priority; fCurrentPriority = (RunMode() == B_OFFLINE) ? min_c(B_NORMAL_PRIORITY, fSetPriority) : fSetPriority; if (fControlThread > 0) { set_thread_priority(fControlThread, fCurrentPriority); fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); printf("BMediaEventLooper: SchedulingLatency is %" B_PRId64 "\n", fSchedulingLatency); } return B_OK; }
status_t CodyCam::_SetUpNodes() { status_t status = B_OK; /* find the media roster */ fMediaRoster = BMediaRoster::Roster(&status); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot find the media roster"), status); return status; } /* find the time source */ status = fMediaRoster->GetTimeSource(&fTimeSourceNode); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot get a time source"), status); return status; } /* find a video producer node */ INFO("CodyCam acquiring VideoInput node\n"); status = fMediaRoster->GetVideoInput(&fProducerNode); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot find a video source. You need " "a webcam to use CodyCam."), status); return status; } /* create the video consumer node */ fVideoConsumer = new VideoConsumer("CodyCam", ((VideoWindow*)fWindow)->VideoView(), ((VideoWindow*)fWindow)->StatusLine(), NULL, 0); if (!fVideoConsumer) { fWindow->ErrorAlert(B_TRANSLATE("Cannot create a video window"), B_ERROR); return B_ERROR; } /* register the node */ status = fMediaRoster->RegisterNode(fVideoConsumer); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot register the video window"), status); return status; } fPort = fVideoConsumer->ControlPort(); /* find free producer output */ int32 cnt = 0; status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1, &cnt, B_MEDIA_RAW_VIDEO); if (status != B_OK || cnt < 1) { status = B_RESOURCE_UNAVAILABLE; fWindow->ErrorAlert(B_TRANSLATE("Cannot find an available video stream"), status); return status; } /* find free consumer input */ cnt = 0; status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(), &fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO); if (status != B_OK || cnt < 1) { status = B_RESOURCE_UNAVAILABLE; fWindow->ErrorAlert(B_TRANSLATE("Can't find an available connection to " "the video window"), status); return status; } /* Connect The Nodes!!! */ media_format format; format.type = B_MEDIA_RAW_VIDEO; media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT, 1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}}; format.u.raw_video = vid_format; /* connect producer to consumer */ status = fMediaRoster->Connect(fProducerOut.source, fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot connect the video source to " "the video window"), status); return status; } /* set time sources */ status = fMediaRoster->SetTimeSourceFor(fProducerNode.node, fTimeSourceNode.node); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the " "video source"), status); return status; } status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(), fTimeSourceNode.node); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the " "video window"), status); return status; } /* figure out what recording delay to use */ bigtime_t latency = 0; status = fMediaRoster->GetLatencyFor(fProducerNode, &latency); status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency); /* start the nodes */ bigtime_t initLatency = 0; status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency); if (status < B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Error getting initial latency for the " "capture node"), status); return status; } initLatency += estimate_max_scheduling_latency(); BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode); bool running = timeSource->IsRunning(); /* workaround for people without sound cards */ /* because the system time source won't be running */ bigtime_t real = BTimeSource::RealTime(); if (!running) { status = fMediaRoster->StartTimeSource(fTimeSourceNode, real); if (status != B_OK) { timeSource->Release(); fWindow->ErrorAlert(B_TRANSLATE("Cannot start time source!"), status); return status; } status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real); if (status != B_OK) { timeSource->Release(); fWindow->ErrorAlert(B_TRANSLATE("Cannot seek time source!"), status); return status; } } bigtime_t perf = timeSource->PerformanceTimeFor(real + latency + initLatency); timeSource->Release(); /* start the nodes */ status = fMediaRoster->StartNode(fProducerNode, perf); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video source"), status); return status; } status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf); if (status != B_OK) { fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video window"), status); return status; } return status; }
status_t NodeManager::_StartNodes() { status_t status = B_NO_INIT; if (!fMediaRoster) return status; bigtime_t latency = 0; bigtime_t initLatency = 0; if (fVideoProducer && fVideoConsumer) { // figure out what recording delay to use status = fMediaRoster->GetLatencyFor(fVideoConnection.producer, &latency); if (status < B_OK) { print_error("error getting latency for video producer", status); } else TRACE("video latency: %Ld\n", latency); status = fMediaRoster->SetProducerRunModeDelay( fVideoConnection.producer, latency); if (status < B_OK) { print_error("error settings run mode delay for video producer", status); } // start the nodes status = fMediaRoster->GetInitialLatencyFor( fVideoConnection.producer, &initLatency); if (status < B_OK) { print_error("error getting initial latency for video producer", status); } } initLatency += estimate_max_scheduling_latency(); if (fAudioProducer) { // TODO: was this supposed to be added to initLatency?!? bigtime_t audioLatency = 0; status = fMediaRoster->GetLatencyFor(fAudioConnection.producer, &audioLatency); TRACE("audio latency: %Ld\n", audioLatency); } BTimeSource* timeSource; if (fVideoProducer) { timeSource = fMediaRoster->MakeTimeSourceFor( fVideoConnection.producer); } else { timeSource = fMediaRoster->MakeTimeSourceFor( fAudioConnection.producer); } bool running = timeSource->IsRunning(); // workaround for people without sound cards // because the system time source won't be running bigtime_t real = BTimeSource::RealTime(); if (!running) { status = fMediaRoster->StartTimeSource(fTimeSource, real); if (status != B_OK) { timeSource->Release(); print_error("cannot start time source!", status); return status; } status = fMediaRoster->SeekTimeSource(fTimeSource, 0, real); if (status != B_OK) { timeSource->Release(); print_error("cannot seek time source!", status); return status; } } bigtime_t perf = timeSource->PerformanceTimeFor(real + latency + initLatency); timeSource->Release(); // start the nodes if (fVideoProducer && fVideoConsumer) { status = fMediaRoster->StartNode(fVideoConnection.consumer, perf); if (status != B_OK) { print_error("Can't start the video consumer", status); return status; } status = fMediaRoster->StartNode(fVideoConnection.producer, perf); if (status != B_OK) { print_error("Can't start the video producer", status); return status; } } if (fAudioProducer) { status = fMediaRoster->StartNode(fAudioConnection.producer, perf); if (status != B_OK) { print_error("Can't start the audio producer", status); return status; } } fPerformanceTimeBase = perf; return status; }
/* virtual */ void BMediaEventLooper::ControlLoop() { CALLED(); status_t err = B_OK; bigtime_t waitUntil = B_INFINITE_TIMEOUT; bool hasRealtime = false; bool hasEvent = false; // While there are no events or it is not time for the earliest event, // process messages using WaitForMessages. Whenever this funtion times out, // we need to handle the next event fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); while (RunState() != B_QUITTING) { if (err == B_TIMED_OUT || err == B_WOULD_BLOCK) { // NOTE: The reference for doing the lateness calculus this way can // be found in the BeBook article "A BMediaEventLooper Example". // The value which we are going to calculate, is referred there as // 'lateness'. media_timed_event event; if (hasEvent) err = fEventQueue.RemoveFirstEvent(&event); else if (hasRealtime) err = fRealTimeQueue.RemoveFirstEvent(&event); if (err == B_OK) { // The general idea of lateness is to allow // the client code to detect when the buffer // is handled late or early. bigtime_t lateness = TimeSource()->RealTime() - waitUntil; DispatchEvent(&event, lateness, hasRealtime); } } else if (err != B_OK) return; // BMediaEventLooper compensates your performance time by adding // the event latency (see SetEventLatency()) and the scheduling // latency (or, for real-time events, only the scheduling latency). hasRealtime = fRealTimeQueue.HasEvents(); hasEvent = fEventQueue.HasEvents(); if (hasEvent) { waitUntil = TimeSource()->RealTimeFor( fEventQueue.FirstEventTime(), fEventLatency + fSchedulingLatency); } else if (!hasRealtime) waitUntil = B_INFINITE_TIMEOUT; if (hasRealtime) { bigtime_t realtimeWait = fRealTimeQueue.FirstEventTime() - fSchedulingLatency; if (!hasEvent || realtimeWait <= waitUntil) { waitUntil = realtimeWait; hasEvent = false; } else hasRealtime = false; } if (waitUntil != B_INFINITE_TIMEOUT && TimeSource()->RealTime() >= waitUntil) { err = WaitForMessage(0); } else err = WaitForMessage(waitUntil); } }