// "This hook function is called when a BBufferConsumer that's receiving data // from you determines that its latency has changed. It will call its // BBufferConsumer::SendLatencyChange() function, and in response, the Media // Server will call your LatencyChanged() function. The source argument // indicates your output that's involved in the connection, and destination // specifies the input on the consumer to which the connection is linked. // newLatency is the consumer's new latency. The flags are currently unused." void AudioFilterNode::LatencyChanged( const media_source& source, const media_destination& destination, bigtime_t newLatency, uint32 flags) { PRINT(("AudioFilterNode::LatencyChanged()\n")); if(source != m_output.source) { PRINT(("\tBad source.\n")); return; } if(destination != m_output.destination) { PRINT(("\tBad destination.\n")); return; } m_downstreamLatency = newLatency; SetEventLatency(m_downstreamLatency + m_processingLatency); if(m_input.source != media_source::null) { // pass new latency upstream status_t err = SendLatencyChange( m_input.source, m_input.destination, EventLatency() + SchedulingLatency()); if(err < B_OK) PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err))); } }
status_t GameProducer::GetLatency(bigtime_t* _latency) { // report our *total* latency: internal plus downstream plus scheduling *_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t ClientNode::GetLatency(bigtime_t *outLatency) { printf("ClientNode::GetLatency\n"); *outLatency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t OffsetFilter::GetLatency(bigtime_t* poLatency) { PRINT(("OffsetFilter::GetLatency()\n")); *poLatency = EventLatency() + SchedulingLatency(); PRINT(("\treturning %Ld\n", *poLatency)); return B_OK; }
status_t FireWireDVNode::GetLatency(bigtime_t* out_latency) { CALLED(); *out_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t FlipTransition::GetLatency(bigtime_t* poLatency) { PRINT(("FlipTransition::GetLatency()\n")); *poLatency = EventLatency() + SchedulingLatency(); PRINT(("\treturning %Ld\n", *poLatency)); return B_OK; }
status_t StepMotionBlurFilter::GetLatency(bigtime_t* poLatency) { PRINT(("StepMotionBlurFilter::GetLatency()\n")); *poLatency = EventLatency() + SchedulingLatency(); PRINT(("\treturning %Ld\n", *poLatency)); return B_OK; }
status_t MediaReader::GetLatency( bigtime_t * out_latency) { CALLED(); *out_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t FlangerNode::GetLatency( bigtime_t* poLatency) { PRINT(("FlangerNode::GetLatency()\n")); *poLatency = EventLatency() + SchedulingLatency(); PRINT(("\treturning %" B_PRIdBIGTIME "\n", *poLatency)); return B_OK; }
status_t AudioProducer::GetLatency(bigtime_t* _latency) { TRACE("%p->AudioProducer::GetLatency()\n", this); // report our *total* latency: internal plus downstream plus scheduling *_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t ToneProducer::GetLatency(bigtime_t* out_latency) { FPRINTF(stderr, "ToneProducer::GetLatency\n"); // report our *total* latency: internal plus downstream plus scheduling *out_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t AudioFilterNode::GetLatency( bigtime_t* outLatency) { PRINT(("AudioFilterNode::GetLatency()\n")); *outLatency = EventLatency() + SchedulingLatency(); PRINT(("\treturning %Ld\n", *outLatency)); return B_OK; }
status_t SoundPlayNode::GetLatency(bigtime_t* _latency) { CALLED(); // report our *total* latency: internal plus downstream plus scheduling *_latency = EventLatency() + SchedulingLatency(); return B_OK; }
void FlangerNode::Connect( status_t status, const media_source& source, const media_destination& destination, const media_format& format, char* pioName) { PRINT(("FlangerNode::Connect()\n")); status_t err; // connection failed? if(status < B_OK) { PRINT(("\tStatus: %s\n", strerror(status))); // 'unreserve' the output m_output.destination = media_destination::null; return; } // connection established: strncpy(pioName, m_output.name, B_MEDIA_NAME_LENGTH); m_output.destination = destination; m_format = format; // figure downstream latency media_node_id timeSource; err = FindLatencyFor(m_output.destination, &m_downstreamLatency, &timeSource); if(err < B_OK) { PRINT(("\t!!! FindLatencyFor(): %s\n", strerror(err))); } PRINT(("\tdownstream latency = %Ld\n", m_downstreamLatency)); // prepare the filter initFilter(); // figure processing time m_processingLatency = calcProcessingLatency(); PRINT(("\tprocessing latency = %Ld\n", m_processingLatency)); // store summed latency SetEventLatency(m_downstreamLatency + m_processingLatency); if(m_input.source != media_source::null) { // pass new latency upstream err = SendLatencyChange( m_input.source, m_input.destination, EventLatency() + SchedulingLatency()); if(err < B_OK) PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err))); } // cache buffer duration SetBufferDuration( buffer_duration( m_format.u.raw_audio)); }
status_t VideoProducer::GetLatency(bigtime_t* _latency) { if (!_latency) return B_BAD_VALUE; *_latency = EventLatency() + SchedulingLatency(); return B_OK; }
void EqualizerNode::LatencyChanged(const media_source &src, const media_destination &dst, bigtime_t latency, uint32 flags) { if (src != fOutputMedia.source || dst != fOutputMedia.destination) return; fDownstreamLatency = latency; SetEventLatency(fDownstreamLatency + fProcessLatency); if (fInputMedia.source != media_source::null) { SendLatencyChange(fInputMedia.source, fInputMedia.destination,EventLatency() + SchedulingLatency()); } }
// [re-]initialize operation if necessary void AudioFilterNode::updateOperation() { if(m_input.source == media_source::null || m_output.destination == media_destination::null) // not fully connected; nothing to do return; // ask the factory for an operation ASSERT(m_opFactory); IAudioOp* op = m_opFactory->createOp( this, m_input.format.u.raw_audio, m_output.format.u.raw_audio); if(!op) { PRINT(( "!!! AudioFilterNode::updateOperation(): no operation created!\n")); // clean up existing operation delete m_op; m_op = 0; return; } // install new operation op->replace(m_op); m_op = op; // do performance tests (what if I'm running? +++++) m_processingLatency = calcProcessingLatency(); PRINT(("\tprocessing latency = %Ld\n", m_processingLatency)); // store summed latency SetEventLatency(m_downstreamLatency + m_processingLatency); // pass new latency upstream status_t err = SendLatencyChange( m_input.source, m_input.destination, EventLatency() + SchedulingLatency()); if(err < B_OK) PRINT(("\t!!! SendLatencyChange(): %s\n", strerror(err))); }
void EqualizerNode::Connect(status_t status, const media_source &src, const media_destination &dst, const media_format &format, char* name) { if (status < B_OK) { fOutputMedia.destination = media_destination::null; return; } strncpy(name, fOutputMedia.name, B_MEDIA_NAME_LENGTH); fOutputMedia.destination = dst; fFormat = format; media_node_id timeSource; FindLatencyFor(fOutputMedia.destination, &fDownstreamLatency, &timeSource); InitFilter(); fProcessLatency = GetFilterLatency(); SetEventLatency(fDownstreamLatency + fProcessLatency); if (fInputMedia.source != media_source::null) { SendLatencyChange(fInputMedia.source, fInputMedia.destination, EventLatency() + SchedulingLatency()); } bigtime_t duration = 0; int sample_size = (fFormat.u.raw_audio.format & 0xf) * fFormat.u.raw_audio.channel_count; if (fFormat.u.raw_audio.buffer_size > 0 && fFormat.u.raw_audio.frame_rate > 0 && sample_size > 0) { duration = (bigtime_t)(((fFormat.u.raw_audio.buffer_size / sample_size) / fFormat.u.raw_audio.frame_rate) * 1000000.0); } SetBufferDuration(duration); }
void LoggingConsumer::BufferReceived(BBuffer* buffer) { bigtime_t bufferStart = buffer->Header()->start_time; bigtime_t now = TimeSource()->Now(); bigtime_t how_early = bufferStart - EventLatency() - SchedulingLatency() - now; log_message logMsg; logMsg.now = now; logMsg.buffer_data.start_time = bufferStart; logMsg.buffer_data.offset = how_early; mLogger->Log(LOG_BUFFER_RECEIVED, logMsg); // There's a special case here with handling B_MEDIA_PARAMETERS buffers. // These contain sets of parameter value changes, with their own performance // times embedded in the buffers. So, we want to dispatch those parameter // changes as their own events rather than pushing this buffer on the queue to // be handled later. if (B_MEDIA_PARAMETERS == buffer->Header()->type) { ApplyParameterData(buffer->Data(), buffer->SizeUsed()); buffer->Recycle(); } else // ahh, it's a regular media buffer, so push it on the event queue { status_t err; media_timed_event event(buffer->Header()->start_time, BTimedEventQueue::B_HANDLE_BUFFER, buffer, BTimedEventQueue::B_RECYCLE_BUFFER); err = EventQueue()->AddEvent(event); // HandleEvent() will recycle the buffer. However, if we incurred an error trying to // put the event into the queue, we have to recycle it ourselves, since HandleEvent() // will never see the buffer in that case. if (err) buffer->Recycle(); } }
status_t EqualizerNode::GetLatency(bigtime_t* latency) { *latency = EventLatency() + SchedulingLatency(); return B_OK; }
void LoggingConsumer::HandleEvent(const media_timed_event *event, bigtime_t /* lateness */, bool /* realTimeEvent */) { log_message logMsg; logMsg.now = TimeSource()->Now(); mLogger->Log(LOG_HANDLE_EVENT, logMsg); switch (event->type) { case BTimedEventQueue::B_HANDLE_BUFFER: { BBuffer* buffer = const_cast<BBuffer*>((BBuffer*) event->pointer); if (buffer) { media_header* hdr = buffer->Header(); if (hdr->destination == mInput.destination.id) { bigtime_t now = TimeSource()->Now(); bigtime_t perf_time = hdr->start_time; // the how_early calculated here doesn't include scheduling latency because // we've already been scheduled to handle the buffer bigtime_t how_early = perf_time - mLatency - now; // logMsg.now is already set logMsg.buffer_data.start_time = perf_time; logMsg.buffer_data.offset = how_early; mLogger->Log(LOG_BUFFER_HANDLED, logMsg); // if the buffer is late, we ignore it and report the fact to the producer // who sent it to us if (how_early < 0) { mLateBuffers++; NotifyLateProducer(mInput.source, -how_early, perf_time); } else { // burn some percentage of our stated latency in CPU time (controlled by // a BParameter). this simulates a user-configurable amount of CPU cost // associated with the consumer. bigtime_t spin_start = ::system_time(); bigtime_t spin_now = spin_start; bigtime_t usecToSpin = bigtime_t(mSpinPercentage / 100.0 * mLatency); while (spin_now - spin_start < usecToSpin) { for (long k = 0; k < 1000000; k++) { /* intentionally blank */ } spin_now = ::system_time(); } } // we're done "processing the buffer;" now we recycle it and return to the loop buffer->Recycle(); } else { //fprintf(stderr, "* Woah! Got a buffer for a different destination!\n"); } } } break; // !!! change to B_PARAMETER as soon as it's available // +++++ e.moon [16jun99] // !!! this can't be right: the parameter value is accessed by the pointer // originally passed to SetParameterValue(). there's no guarantee that // value's still valid, is there? case BTimedEventQueue::B_USER_EVENT: { size_t dataSize = size_t(event->data); int32 param = int32(event->bigdata); logMsg.param.id = param; // handle the message if there's sufficient data provided. we only check against // sizeof(float) because all of our parameters happen to be 4 bytes. if various // parameters took different amounts of data, we'd check the size on a per-parameter // basis. if (dataSize >= sizeof(float)) switch (param) { case LATENCY_PARAM: { float value = *((float*) event->pointer); mLatency = bigtime_t(value* 1000); mLastLatencyChange = logMsg.now; // my latency just changed, so reconfigure the BMediaEventLooper // to give me my events at the proper time SetEventLatency(mLatency); // tell the producer that my latency changed, and broadcast a message // about the parameter change to any applications that may be looking // for it through the BMediaRoster::StartWatching() mechanism. // // if we had more than one input, we'd need to tell *all* producers about // the change in our latency. SendLatencyChange(mInput.source, mInput.destination, EventLatency() + SchedulingLatency()); BroadcastNewParameterValue(logMsg.now, param, &value, sizeof(value)); // log the new latency value, for recordkeeping logMsg.param.value = value; mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg); } break; case CPU_SPIN_PARAM: { float value = *((float*) event->pointer); mSpinPercentage = value; mLastSpinChange = logMsg.now; BroadcastNewParameterValue(logMsg.now, param, &value, sizeof(value)); logMsg.param.value = value; mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg); } break; case PRIORITY_PARAM: { mPriority = *((int32*) event->pointer); // DO NOT use ::set_thead_priority() to directly alter the node's control // thread priority. BMediaEventLooper tracks the priority itself and recalculates // the node's scheduling latency whenever SetPriority() is called. This is VERY // important for correct functioning of a node chain. You should *only* alter a // BMediaEventLooper's priority by calling its SetPriority() method. SetPriority(mPriority); mLastPrioChange = logMsg.now; BroadcastNewParameterValue(logMsg.now, param, &mPriority, sizeof(mPriority)); logMsg.param.value = (float) mPriority; mLogger->Log(LOG_SET_PARAM_HANDLED, logMsg); } break; // log the fact that we "handled" a "set parameter" event for a // nonexistent parameter default: mLogger->Log(LOG_INVALID_PARAM_HANDLED, logMsg); break; } } break; case BTimedEventQueue::B_START: // okay, let's go! mLogger->Log(LOG_START_HANDLED, logMsg); break; case BTimedEventQueue::B_STOP: mLogger->Log(LOG_STOP_HANDLED, logMsg); // stopping implies not handling any more buffers. So, we flush all pending // buffers out of the event queue before returning to the event loop. EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); break; case BTimedEventQueue::B_SEEK: // seeking the log doesn't make any sense, so we just log that we handled the seek // and return without doing anything else mLogger->Log(LOG_SEEK_HANDLED, logMsg); break; case BTimedEventQueue::B_WARP: // similarly, time warps aren't meaningful to the logger, so just record it and return mLogger->Log(LOG_WARP_HANDLED, logMsg); break; case BTimedEventQueue::B_DATA_STATUS: // we really don't care about the producer's data status, but this is where // we'd do something about it if we did. logMsg.data_status.status = event->data; mLogger->Log(LOG_DATA_STATUS_HANDLED, logMsg); break; default: // hmm, someone enqueued a message that we don't understand. log and ignore it. logMsg.unknown.what = event->type; mLogger->Log(LOG_HANDLE_UNKNOWN, logMsg); break; } }
void VideoConsumer::_HandleBuffer(BBuffer* buffer) { if (RunState() != B_STARTED || !fConnectionActive) { TRACE("RunState() != B_STARTED\n"); buffer->Recycle(); return; } // See if this is one of our BBitmap buffers uint32 index = 0; fOurBuffers = true; while (index < kBufferCount) { if (buffer->ID() == fBufferMap[index]->ID()) break; else index++; } if (index == kBufferCount) { // Buffers belong to consumer // NOTE: We maintain this in a member variable, since we still need // to recycle this buffer later on, in case it was the last buffer // received before shutting down. fOurBuffers = false; index = (fLastBufferIndex + 1) % kBufferCount; } bool recycle = true; bigtime_t now = TimeSource()->Now(); if (RunMode() == B_OFFLINE || now < buffer->Header()->start_time + kMaxBufferLateness) { // Only display the buffer if it's not too late, or if we are // in B_OFFLINE run-mode. if (!fOurBuffers) { memcpy(fBitmap[index]->Bits(), buffer->Data(), fBitmap[index]->BitsLength()); } bigtime_t tooEarly = buffer->Header()->start_time - now; if (tooEarly > 3000) snooze(tooEarly); fTargetLock.Lock(); if (fTarget) { fTarget->SetBitmap(fBitmap[index]); if (fOurBuffers) { // recycle the previous but not the current buffer if (fLastBufferIndex >= 0) fBufferMap[fLastBufferIndex]->Recycle(); recycle = false; } fLastBufferIndex = index; } fTargetLock.Unlock(); } else { // Drop the buffer if it's too late. if (fManager->LockWithTimeout(10000) == B_OK) { fManager->FrameDropped(); fManager->Unlock(); } PROGRESS("VideoConsumer::HandleEvent - DROPPED FRAME\n" " start_time: %lld, current: %lld, latency: %lld\n", buffer->Header()->start_time, TimeSource()->Now(), SchedulingLatency()); } if (recycle) buffer->Recycle(); }
// GetLatency status_t VideoProducer::GetLatency(bigtime_t* out_latency) { *out_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t EMBeOutputNode::GetLatency(bigtime_t* out_latency) { // report our *total* latency: internal plus downstream plus scheduling (*out_latency) = EventLatency() + SchedulingLatency(); return B_OK; }