status_t GameProducer::GetLatency(bigtime_t* _latency) { // report our *total* latency: internal plus downstream plus scheduling *_latency = EventLatency() + SchedulingLatency(); return B_OK; }
status_t ESDSinkNode::GetLatencyFor( const media_destination & for_whom, bigtime_t * out_latency, media_node_id * out_timesource) { CALLED(); if ((out_latency == 0) || (out_timesource == 0)) { fprintf(stderr,"<- B_BAD_VALUE\n"); return B_BAD_VALUE; } if(fInput.destination != for_whom) { fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); return B_MEDIA_BAD_DESTINATION; } bigtime_t intl = EventLatency(); bigtime_t netl = 0LL; if (fDevice) netl = fDevice->Latency(); // I don't want to swap if (netl > 500000) netl = 500000; *out_latency = intl + netl; fprintf(stderr, "int latency %Ld, net latency %Ld, total latency %Ld\n", intl, netl, *out_latency); *out_timesource = TimeSource()->ID(); return B_OK; }
// "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 StepMotionBlurFilter::LatencyChanged( const media_source& source, const media_destination& destination, bigtime_t newLatency, uint32 flags) { PRINT(("StepMotionBlurFilter::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 ClientNode::GetLatency(bigtime_t *outLatency) { printf("ClientNode::GetLatency\n"); *outLatency = 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 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 MediaReader::GetLatency( bigtime_t * out_latency) { CALLED(); *out_latency = EventLatency() + SchedulingLatency(); 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 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; }
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 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 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; }
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; }
status_t AudioConsumer::GetLatencyFor(const media_destination& dst, bigtime_t* latency, media_node_id* time_src) { // we have multiple inputs with different IDs, but // the port number must match our ControlPort() if (dst.port != ControlPort()) return B_MEDIA_BAD_DESTINATION; *latency = EventLatency(); *time_src += TimeSource()->ID(); 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))); }
// how should we handle late buffers? drop them? // notify the producer? status_t ESDSinkNode::HandleBuffer( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); BBuffer * buffer = const_cast<BBuffer*>((BBuffer*)event->pointer); if (buffer == 0) { fprintf(stderr,"<- B_BAD_VALUE\n"); return B_BAD_VALUE; } if(fInput.destination.id != buffer->Header()->destination) { fprintf(stderr,"<- B_MEDIA_BAD_DESTINATION\n"); return B_MEDIA_BAD_DESTINATION; } media_header* hdr = buffer->Header(); bigtime_t now = TimeSource()->Now(); bigtime_t perf_time = hdr->start_time; // the how_early calculate here doesn't include scheduling latency because // we've already been scheduled to handle the buffer bigtime_t how_early = perf_time - EventLatency() - now; // if the buffer is late, we ignore it and report the fact to the producer // who sent it to us if ((RunMode() != B_OFFLINE) && // lateness doesn't matter in offline mode... (RunMode() != B_RECORDING) && // ...or in recording mode (how_early < 0LL)) { //mLateBuffers++; NotifyLateProducer(fInput.source, -how_early, perf_time); fprintf(stderr," <- LATE BUFFER : %lli\n", how_early); buffer->Recycle(); } else { if (fDevice->CanSend()) fDevice->Write(buffer->Data(), buffer->SizeUsed()); } return B_OK; }
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 ClientNode::HandleEvent(const media_timed_event *event, bigtime_t late, bool realTimeEvent) { //printf("ClientNode::HandleEvent %d\n", event->type); switch (event->type) { case BTimedEventQueue::B_HANDLE_BUFFER: { printf("BTimedEventQueue::B_HANDLE_BUFFER\n"); break; } case BTimedEventQueue::B_START: { printf("BTimedEventQueue::B_START\n"); if (RunState() != B_STARTED) { fFramesSent = 0; fTime = TimeSource()->RealTime(); int period = (fOwner->GetOutputPorts()->CountItems())*3; fBufferGroup = new BBufferGroup(fFormat.u.raw_audio.buffer_size, period); if (fBufferGroup->InitCheck() != B_OK) printf("error\n"); bigtime_t start = ::system_time(); ComputeCycle(); bigtime_t produceLatency = ::system_time(); fProcessLatency = produceLatency - start; printf("Estimated latency is %Ld\n", fProcessLatency); JackPortList* outputPorts = fOwner->GetOutputPorts(); for (int i = 0; i < outputPorts->CountItems(); i++) { JackPort* port = outputPorts->ItemAt(i); port->CurrentBuffer()->Recycle(); } int sample_size = (fFormat.u.raw_audio.format & 0xf)* fFormat.u.raw_audio.channel_count; bigtime_t duration = bigtime_t(1000000) * fOwner->BufferSize() / bigtime_t(fFormat.u.raw_audio.frame_rate); SetBufferDuration(duration); SetEventLatency(fDownstreamLatency + fProcessLatency); _ScheduleOutputEvent(fTime); } break; } case BTimedEventQueue::B_STOP: { // stopped - don't process any more buffers, flush all buffers // from event queue EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); Stop(TimeSource()->Now(), true); NodeStopped(TimeSource()->Now()); break; } case BTimedEventQueue::B_DATA_STATUS: { break; } case NEW_BUFFER_EVENT: { ComputeCycle(); _DataAvailable(event->event_time); // Now we schedule the next event bigtime_t nextEvent = fTime + bigtime_t((1000000LL * fFramesSent) / (int32)fFormat.u.raw_audio.frame_rate)+EventLatency(); _ScheduleOutputEvent(nextEvent); break; } default: break; } }
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; } }
// create or discard buffer group if necessary void AudioFilterNode::updateBufferGroup() { status_t err; size_t inputSize = bytes_per_frame(m_input.format.u.raw_audio); size_t outputSize = bytes_per_frame(m_output.format.u.raw_audio); if(m_input.source == media_source::null || m_output.destination == media_destination::null || inputSize >= outputSize) { PRINT(("###### NO BUFFER GROUP NEEDED\n")); // no internal buffer group needed if(m_bufferGroup) { // does this block? +++++ delete m_bufferGroup; m_bufferGroup = 0; } return; } int32 bufferCount = EventLatency() / BufferDuration() + 1 + 1; // +++++ // [e.moon 27sep99] this is a reasonable number of buffers, // but it fails with looped file-player node in BeOS 4.5.2. // if(bufferCount < 5) bufferCount = 5; // if(bufferCount < 3) // bufferCount = 3; if(m_bufferGroup) { // is the current group sufficient? int32 curBufferCount; err = m_bufferGroup->CountBuffers(&curBufferCount); if(err == B_OK && curBufferCount >= bufferCount) { BBuffer* buf = m_bufferGroup->RequestBuffer( outputSize, -1); if(buf) { // yup buf->Recycle(); return; } } // nope, delete it to make way for the new one delete m_bufferGroup; m_bufferGroup = 0; } // create buffer group PRINT(( "##### AudioFilterNode::updateBufferGroup():\n" "##### creating %ld buffers of size %ld\n", bufferCount, m_output.format.u.raw_audio.buffer_size)); m_bufferGroup = new BBufferGroup( m_output.format.u.raw_audio.buffer_size, bufferCount); }
// 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; }