void ProducerNode::BufferProducer() { // this thread produces one buffer each two seconds, // and shedules it to be handled one second later than produced // assuming a realtime timesource status_t rv; for (;;) { rv = acquire_sem_etc(mBufferProducerSem,1,B_RELATIVE_TIMEOUT,DELAY); if (rv == B_INTERRUPTED) { continue; } else if (rv == B_OK) { // triggered by AdditionalBufferRequested release_sem(mBufferProducerSem); } else if (rv != B_TIMED_OUT) { // triggered by deleting the semaphore (stop request) break; } if (!mOutputEnabled) continue; BBuffer *buffer; // out("ProducerNode: RequestBuffer\n"); buffer = mBufferGroup->RequestBuffer(2048); if (!buffer) { } buffer->Header()->start_time = TimeSource()->Now() + DELAY / 2; out("ProducerNode: SendBuffer, sheduled time = %5.4f\n",buffer->Header()->start_time / 1E6); rv = SendBuffer(buffer, mOutput.destination); if (rv != B_OK) { } } }
bigtime_t EqualizerNode::GetFilterLatency(void) { if (fOutputMedia.destination == media_destination::null) return 0LL; BBufferGroup* test_group = new BBufferGroup(fOutputMedia.format.u.raw_audio.buffer_size, 1); BBuffer* buffer = test_group->RequestBuffer(fOutputMedia.format.u.raw_audio.buffer_size); buffer->Header()->type = B_MEDIA_RAW_AUDIO; buffer->Header()->size_used = fOutputMedia.format.u.raw_audio.buffer_size; bigtime_t begin = system_time(); FilterBuffer(buffer); bigtime_t latency = system_time() - begin; buffer->Recycle(); delete test_group; InitFilter(); return latency; }
// figure processing latency by doing 'dry runs' of filterBuffer() bigtime_t StepMotionBlurFilter::calcProcessingLatency() { PRINT(("StepMotionBlurFilter::calcProcessingLatency()\n")); if(m_output.destination == media_destination::null) { PRINT(("\tNot connected.\n")); return 0LL; } // allocate a temporary buffer group BBufferGroup* pTestGroup = new BBufferGroup(m_output.format.u.raw_video.display.line_width * m_output.format.u.raw_video.display.line_count *4, 1); // fetch a buffer BBuffer* pBuffer = pTestGroup->RequestBuffer(m_output.format.u.raw_video.display.line_width * m_output.format.u.raw_video.display.line_count * 4); ASSERT(pBuffer); pBuffer->Header()->type = B_MEDIA_RAW_VIDEO; pBuffer->Header()->size_used = m_output.format.u.raw_video.display.line_width * m_output.format.u.raw_video.display.line_count * 4; // run the test bigtime_t preTest = system_time(); filterBuffer(pBuffer); bigtime_t elapsed = system_time()-preTest; // clean up pBuffer->Recycle(); delete pTestGroup; // reset filter state initFilter(); return elapsed; }
// -------------------------------------------------------- // // implementation for BMediaEventLooper // -------------------------------------------------------- // // protected: status_t MediaReader::HandleBuffer( const media_timed_event *event, bigtime_t lateness, bool realTimeEvent) { CALLED(); if (output.destination == media_destination::null) return B_MEDIA_NOT_CONNECTED; status_t status = B_OK; BBuffer * buffer = fBufferGroup->RequestBuffer(output.format.u.multistream.max_chunk_size,fBufferPeriod); if (buffer != 0) { status = FillFileBuffer(buffer); if (status != B_OK) { PRINT("MediaReader::HandleEvent got an error from FillFileBuffer.\n"); buffer->Recycle(); } else { if (fOutputEnabled) { status = SendBuffer(buffer,output.destination); if (status != B_OK) { PRINT("MediaReader::HandleEvent got an error from SendBuffer.\n"); buffer->Recycle(); } } else { buffer->Recycle(); } } } bigtime_t nextEventTime = event->event_time+fBufferPeriod; media_timed_event nextBufferEvent(nextEventTime, BTimedEventQueue::B_HANDLE_BUFFER); EventQueue()->AddEvent(nextBufferEvent); return status; }
void ClientNode::_DataAvailable(bigtime_t time) { size_t samples = fFormat.u.raw_audio.buffer_size / sizeof(float); fFramesSent += samples; JackPortList* ports = fOwner->GetOutputPorts(); for (int i = 0; i < ports->CountItems(); i++) { JackPort* port = ports->ItemAt(i); if (port != NULL && port->IsConnected()) { BBuffer* buffer = FillNextBuffer(time, port); if (buffer) { if (SendBuffer(buffer, port->MediaOutput()->source, port->MediaOutput()->destination) != B_OK) { printf("ClientNode::_DataAvailable: Buffer sending " "failed\n"); buffer->Recycle(); } size_t nFrames = fFormat.u.raw_audio.buffer_size / ((fFormat.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) * fFormat.u.raw_audio.channel_count); } if (buffer == NULL) return; } } }
BBuffer* SoundPlayNode::FillNextBuffer(bigtime_t eventTime) { CALLED(); // get a buffer from our buffer group BBuffer* buffer = fBufferGroup->RequestBuffer( fOutput.format.u.raw_audio.buffer_size, BufferDuration() / 2); // If we fail to get a buffer (for example, if the request times out), we // skip this buffer and go on to the next, to avoid locking up the control // thread if (buffer == NULL) { ERROR("SoundPlayNode::FillNextBuffer: RequestBuffer failed\n"); return NULL; } if (fPlayer->HasData()) { fPlayer->PlayBuffer(buffer->Data(), fOutput.format.u.raw_audio.buffer_size, fOutput.format.u.raw_audio); } else memset(buffer->Data(), 0, fOutput.format.u.raw_audio.buffer_size); // fill in the buffer header media_header* header = buffer->Header(); header->type = B_MEDIA_RAW_AUDIO; header->size_used = fOutput.format.u.raw_audio.buffer_size; header->time_source = TimeSource()->ID(); header->start_time = eventTime; return buffer; }
// figure processing latency by doing 'dry runs' of filterBuffer() bigtime_t FlangerNode::calcProcessingLatency() { PRINT(("FlangerNode::calcProcessingLatency()\n")); if(m_output.destination == media_destination::null) { PRINT(("\tNot connected.\n")); return 0LL; } // allocate a temporary buffer group BBufferGroup* pTestGroup = new BBufferGroup( m_output.format.u.raw_audio.buffer_size, 1); // fetch a buffer BBuffer* pBuffer = pTestGroup->RequestBuffer( m_output.format.u.raw_audio.buffer_size); ASSERT(pBuffer); pBuffer->Header()->type = B_MEDIA_RAW_AUDIO; pBuffer->Header()->size_used = m_output.format.u.raw_audio.buffer_size; // run the test bigtime_t preTest = system_time(); filterBuffer(pBuffer); bigtime_t elapsed = system_time()-preTest; // clean up pBuffer->Recycle(); delete pTestGroup; // reset filter state initFilter(); return elapsed; }
BINLINE void BMessageHeader::read(BBuffer& bbuf) { bbuf.setByteOrder(BBIG_ENDIAN); bool cmpr = bbuf.setCompressInteger(false); bbuf.serialize(magic); switch(magic) { case BMAGIC_BINARY_STREAM: break; case BMAGIC_BINARY_STREAM_LE: magic = BMAGIC_BINARY_STREAM; bbuf.setByteOrder(BLITTLE_ENDIAN); break; default: throw BException(BExceptionC::CORRUPT, L"Stream must start with BYPS or SPYB"); } bbuf.serialize(error); bbuf.serialize(flags); if (flags & BHEADER_FLAG_BYPS_VERSION) { bbuf.serialize(bversion); } bbuf.serialize(version); targetId.serialize(bbuf, bversion); bbuf.serialize(messageId); bbuf.setCompressInteger(cmpr); if (bversion >= BHEADER_BYPS_VERSION_WITH_SESSIONID) { sessionId = BTargetId::readSessionId(bbuf); } }
void FireWireDVNode::card_reader_thread() { status_t err; size_t rbufsize; int rcount; fCard->GetBufInfo(&rbufsize, &rcount); delete fBufferGroupEncVideo; fBufferGroupEncVideo = new BBufferGroup(rbufsize, rcount); while (!fTerminateThreads) { void *data, *end; ssize_t sizeUsed = fCard->Read(&data); if (sizeUsed < 0) { TRACE("FireWireDVNode::%s: %s\n", __FUNCTION__, strerror(sizeUsed)); continue; } end = (char*)data + sizeUsed; while (data < end) { BBuffer* buf = fBufferGroupEncVideo->RequestBuffer(rbufsize, 10000); if (!buf) { TRACE("OutVideo: request buffer timout\n"); continue; } err = fCard->Extract(buf->Data(), &data, &sizeUsed); if (err) { buf->Recycle(); printf("OutVideo Extract error %s\n", strerror(err)); continue; } media_header* hdr = buf->Header(); hdr->type = B_MEDIA_ENCODED_VIDEO; hdr->size_used = sizeUsed; hdr->time_source = TimeSource()->ID(); // set time source id //what should the start_time be? hdr->start_time = TimeSource()->PerformanceTimeFor(system_time()); fLock.Lock(); if (SendBuffer(buf, fOutputEncVideo.source, fOutputEncVideo.destination) != B_OK) { TRACE("OutVideo: sending buffer failed\n"); buf->Recycle(); } fLock.Unlock(); } } }
BBuffer* GameProducer::FillNextBuffer(bigtime_t event_time) { // get a buffer from our buffer group BBuffer* buf = fBufferGroup->RequestBuffer(fBufferSize, BufferDuration()); // if we fail to get a buffer (for example, if the request times out), we // skip this buffer and go on to the next, to avoid locking up the control // thread. if (!buf) return NULL; // we need to discribe the buffer int64 frames = int64(fBufferSize / fFrameSize); memset(buf->Data(), 0, fBufferSize); // now fill the buffer with data, continuing where the last buffer left off fObject->Play(buf->Data(), frames); // fill in the buffer header media_header* hdr = buf->Header(); hdr->type = B_MEDIA_RAW_AUDIO; hdr->size_used = fBufferSize; hdr->time_source = TimeSource()->ID(); bigtime_t stamp; if (RunMode() == B_RECORDING) { // In B_RECORDING mode, we stamp with the capture time. We're not // really a hardware capture node, but we simulate it by using the // (precalculated) time at which this buffer "should" have been created. stamp = event_time; } else { // okay, we're in one of the "live" performance run modes. in these // modes, we stamp the buffer with the time at which the buffer should // be rendered to the output, not with the capture time. fStartTime is // the cached value of the first buffer's performance time; we calculate // this buffer's performance time as an offset from that time, based on // the amount of media we've created so far. // Recalculating every buffer like this avoids accumulation of error. stamp = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); } hdr->start_time = stamp; return buf; }
// figure processing latency by doing 'dry runs' of processBuffer() bigtime_t AudioFilterNode::calcProcessingLatency() { PRINT(("AudioFilterNode::calcProcessingLatency()\n")); ASSERT(m_input.source != media_source::null); ASSERT(m_output.destination != media_destination::null); ASSERT(m_op); // initialize filter m_op->init(); size_t maxSize = max_c( m_input.format.u.raw_audio.buffer_size, m_output.format.u.raw_audio.buffer_size); // allocate a temporary buffer group BBufferGroup* testGroup = new BBufferGroup( maxSize, 1); // fetch a buffer big enough for in-place processing BBuffer* buffer = testGroup->RequestBuffer( maxSize, -1); ASSERT(buffer); buffer->Header()->type = B_MEDIA_RAW_AUDIO; buffer->Header()->size_used = m_input.format.u.raw_audio.buffer_size; // run the test bigtime_t preTest = system_time(); processBuffer(buffer, buffer); bigtime_t elapsed = system_time()-preTest; // clean up buffer->Recycle(); delete testGroup; // reset filter state m_op->init(); return elapsed;// + 100000LL; }
void AudioConsumer::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"); if (RunState() == B_STARTED) { // log latency BBuffer* buffer = (BBuffer*)event->pointer; buffer->Recycle(); } break; } default: break; } }
BBuffer* ClientNode::FillNextBuffer(bigtime_t eventTime, JackPort* port) { //printf("FillNextBuffer\n"); BBuffer* buffer = port->CurrentBuffer(); media_header* header = buffer->Header(); header->type = B_MEDIA_RAW_AUDIO; header->size_used = fFormat.u.raw_audio.buffer_size; header->time_source = TimeSource()->ID(); bigtime_t start; if (RunMode() == B_RECORDING) start = eventTime; else start = fTime + bigtime_t(double(fFramesSent) / double(fFormat.u.raw_audio.frame_rate) * 1000000.0); header->start_time = start; return buffer; }
status_t ClientNode::_InitOutputPorts() { //printf("JackClient::_InitOutputPorts()\n"); JackPortList* outputPorts = fOwner->GetOutputPorts(); for (int i = 0; i < outputPorts->CountItems(); i++) { JackPort* port = outputPorts->ItemAt(i); if (!port->IsConnected()) return B_ERROR; BBuffer* buffer = fBufferGroup->RequestBuffer( fFormat.u.raw_audio.buffer_size); if (buffer == NULL || buffer->Data() == NULL) { printf("RequestBuffer failed\n"); return B_ERROR; } port->SetProcessingBuffer(buffer); } return B_OK; }
BINLINE void BMessageHeader::write(BBuffer& bbuf) { bool cmpr = bbuf.setCompressInteger(false); bbuf.setByteOrder(byteOrder); bbuf.serialize(magic); bbuf.serialize(error); bbuf.serialize(flags); if (flags & BHEADER_FLAG_BYPS_VERSION) { bbuf.serialize(bversion); } bbuf.serialize(version); targetId.serialize(bbuf, bversion); bbuf.serialize(messageId); bbuf.setCompressInteger(cmpr); if (bversion >= BHEADER_BYPS_VERSION_WITH_SESSIONID) { BTargetId::writeSessionId(bbuf, sessionId); } }
// 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; }
// _FrameGenerator int32 VideoProducer::_FrameGenerator() { bool forceSendingBuffer = true; bigtime_t lastFrameSentAt = 0; bool running = true; while (running) { ldebug("VideoProducer: loop: %Ld\n", fFrame); // lock the node manager status_t err = fManager->LockWithTimeout(10000); bool ignoreEvent = false; // Data to be retrieved from the node manager. bigtime_t performanceTime = 0; bigtime_t nextPerformanceTime = 0; bigtime_t waitUntil = 0; bigtime_t nextWaitUntil = 0; bigtime_t maxRenderTime = 0; int32 playingDirection = 0; int64 playlistFrame = 0; switch (err) { case B_OK: { ldebug("VideoProducer: node manager successfully locked\n"); // get the times for the current and the next frame performanceTime = fManager->TimeForFrame(fFrame); nextPerformanceTime = fManager->TimeForFrame(fFrame + 1); maxRenderTime = min_c(bigtime_t(33334 * 0.9), max_c(fSupplier->ProcessingLatency(), maxRenderTime)); waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase + performanceTime, 0) - maxRenderTime; nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase + nextPerformanceTime, 0) - maxRenderTime; // get playing direction and playlist frame for the current frame bool newPlayingState; playlistFrame = fManager->PlaylistFrameAtFrame(fFrame, playingDirection, newPlayingState); ldebug("VideoProducer: performance time: %Ld, playlist frame: %Ld\n", performanceTime, playlistFrame); forceSendingBuffer |= newPlayingState; fManager->SetCurrentVideoTime(nextPerformanceTime); fManager->Unlock(); break; } case B_TIMED_OUT: ldebug("VideoProducer: Couldn't lock the node manager.\n"); ignoreEvent = true; waitUntil = system_time() - 1; break; default: printf("Couldn't lock the node manager. Terminating video producer " "frame generator thread.\n"); ignoreEvent = true; waitUntil = system_time() - 1; running = false; break; } // Force sending a frame, if the last one has been sent more than // one second ago. if (lastFrameSentAt + 1000000 < performanceTime) forceSendingBuffer = true; ldebug("VideoProducer: waiting (%Ld)...\n", waitUntil); // wait until... err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil); // The only acceptable responses are B_OK and B_TIMED_OUT. Everything // else means the thread should quit. Deleting the semaphore, as in // VideoProducer::_HandleStop(), will trigger this behavior. switch (err) { case B_OK: ldebug("VideoProducer::_FrameGenerator - going back to sleep.\n"); break; case B_TIMED_OUT: ldebug("VideoProducer: timed out => event\n"); // Catch the cases in which the node manager could not be // locked and we therefore have no valid data to work with, // or the producer is not running or enabled. if (ignoreEvent || !fRunning || !fEnabled) { ldebug("VideoProducer: ignore event\n"); // nothing to do // Drop frame if it's at least a frame late. } else if (nextWaitUntil < system_time()) { //printf("VideoProducer: dropped frame (%ld)\n", fFrame); if (fManager->LockWithTimeout(10000) == B_OK) { fManager->FrameDropped(); fManager->Unlock(); } // next frame fFrame++; // Send buffers only, if playing, the node is running and the // output has been enabled } else if (playingDirection != 0 || forceSendingBuffer) { ldebug("VideoProducer: produce frame\n"); BAutolock _(fLock); // Fetch a buffer from the buffer group BBuffer *buffer = fUsedBufferGroup->RequestBuffer( fConnectedFormat.display.bytes_per_row * fConnectedFormat.display.line_count, 0LL); if (buffer) { // Fill out the details about this buffer. media_header *h = buffer->Header(); h->type = B_MEDIA_RAW_VIDEO; h->time_source = TimeSource()->ID(); h->size_used = fConnectedFormat.display.bytes_per_row * fConnectedFormat.display.line_count; // For a buffer originating from a device, you might // want to calculate this based on the // PerformanceTimeFor the time your buffer arrived at // the hardware (plus any applicable adjustments). h->start_time = fPerformanceTimeBase + performanceTime; h->file_pos = 0; h->orig_size = 0; h->data_offset = 0; h->u.raw_video.field_gamma = 1.0; h->u.raw_video.field_sequence = fFrame; h->u.raw_video.field_number = 0; h->u.raw_video.pulldown_number = 0; h->u.raw_video.first_active_line = 1; h->u.raw_video.line_count = fConnectedFormat.display.line_count; // Fill in a frame media_format mf; mf.type = B_MEDIA_RAW_VIDEO; mf.u.raw_video = fConnectedFormat; ldebug("VideoProducer: frame: %Ld, playlistFrame: %Ld\n", fFrame, playlistFrame); bool forceOrWasCached = forceSendingBuffer; // if (fManager->LockWithTimeout(5000) == B_OK) { // we need to lock the manager, or our // fSupplier might work on bad data err = fSupplier->FillBuffer(playlistFrame, buffer->Data(), &mf, forceOrWasCached); // fManager->Unlock(); // } else { // err = B_ERROR; // } // clean the buffer if something went wrong if (err != B_OK) { memset(buffer->Data(), 0, h->size_used); err = B_OK; } // Send the buffer on down to the consumer if (!forceOrWasCached) { if (SendBuffer(buffer, fOutput.source, fOutput.destination) != B_OK) { printf("_FrameGenerator: Error sending buffer\n"); // If there is a problem sending the buffer, // or if we don't send the buffer because its // contents are the same as the last one, // return it to its buffer group. buffer->Recycle(); // we tell the supplier to delete // its caches if there was a problem sending // the buffer fSupplier->DeleteCaches(); } } else buffer->Recycle(); // Only if everything went fine we clear the flag // that forces us to send a buffer even if not // playing. if (err == B_OK) { forceSendingBuffer = false; lastFrameSentAt = performanceTime; } } else ldebug("no buffer!\n"); // next frame fFrame++; } else { ldebug("VideoProducer: not playing\n"); // next frame fFrame++; } break; default: ldebug("Couldn't acquire semaphore. Error: %s\n", strerror(err)); running = false; break; } } ldebug("VideoProducer: frame generator thread done.\n"); return B_OK; }
BBuffer* AudioProducer::_FillNextBuffer(bigtime_t eventTime) { BBuffer* buffer = fBufferGroup->RequestBuffer( fOutput.format.u.raw_audio.buffer_size, BufferDuration()); if (!buffer) { ERROR("AudioProducer::_FillNextBuffer() - no buffer\n"); return NULL; } size_t sampleSize = fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK; size_t numSamples = fOutput.format.u.raw_audio.buffer_size / sampleSize; // number of sample in the buffer // fill in the buffer header media_header* header = buffer->Header(); header->type = B_MEDIA_RAW_AUDIO; header->time_source = TimeSource()->ID(); buffer->SetSizeUsed(fOutput.format.u.raw_audio.buffer_size); bigtime_t performanceTime = bigtime_t(double(fFramesSent) * 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate)); // fill in data from audio supplier int64 frameCount = numSamples / fOutput.format.u.raw_audio.channel_count; bigtime_t startTime = performanceTime; bigtime_t endTime = bigtime_t(double(fFramesSent + frameCount) * 1000000.0 / fOutput.format.u.raw_audio.frame_rate); if (!fSupplier || fSupplier->InitCheck() != B_OK || fSupplier->GetFrames(buffer->Data(), frameCount, startTime, endTime) != B_OK) { ERROR("AudioProducer::_FillNextBuffer() - supplier error -> silence\n"); memset(buffer->Data(), 0, buffer->SizeUsed()); } // stamp buffer if (RunMode() == B_RECORDING) { header->start_time = eventTime; } else { header->start_time = fStartTime + performanceTime; } #if DEBUG_TO_FILE BMediaTrack* track; if (BMediaFile* file = init_media_file(fOutput.format, &track)) { track->WriteFrames(buffer->Data(), frameCount); } #endif // DEBUG_TO_FILE if (fPeakListener && fOutput.format.u.raw_audio.format == media_raw_audio_format::B_AUDIO_FLOAT) { // TODO: extend the peak notifier for other sample formats int32 channels = fOutput.format.u.raw_audio.channel_count; float max[channels]; float min[channels]; for (int32 i = 0; i < channels; i++) { max[i] = -1.0; min[i] = 1.0; } float* sample = (float*)buffer->Data(); for (uint32 i = 0; i < frameCount; i++) { for (int32 k = 0; k < channels; k++) { if (*sample < min[k]) min[k] = *sample; if (*sample > max[k]) max[k] = *sample; sample++; } } BMessage message(MSG_PEAK_NOTIFICATION); for (int32 i = 0; i < channels; i++) { float maxAbs = max_c(fabs(min[i]), fabs(max[i])); message.AddFloat("max", maxAbs); } bigtime_t realTime = TimeSource()->RealTimeFor( fStartTime + performanceTime, 0); MessageEvent* event = new (std::nothrow) MessageEvent(realTime, fPeakListener, message); if (event != NULL) EventQueue::Default().AddEvent(event); } return buffer; }
void TVideoPreviewView::DisplayThread() { FUNCTION("TVideoPreviewView::DisplayThread\n"); bigtime_t timeout = 5000; bigtime_t realTimeNow = 0; bigtime_t perfTimeNow = 0; bigtime_t halfPeriod = (bigtime_t) (500000./29.97); bool timeSourceRunning = false; while (!mDisplayQuit) { if (acquire_sem(mServiceLock) == B_NO_ERROR) { timeSourceRunning = TimeSource()->IsRunning(); realTimeNow = BTimeSource::RealTime(); perfTimeNow = TimeSource()->Now(); release_sem(mServiceLock); } snooze(timeout); if (timeSourceRunning) { // if we received a Stop, deal with it if (mStopping) { PROGRESS("VidConsumer::DisplayThread - STOP\n"); if (perfTimeNow >= mStopTime) { mRunning = false; mStopping = false; // deal with any pending Seek if (mSeeking) mSeeking = false; //if (mConnected) // SendDataStatus(B_DATA_NOT_AVAILABLE, mConnections[0], mStopTime); continue; } } // if we received a Seek, deal with it if (mSeeking) { PROGRESS("VidConsumer::DisplayThread - SEEK\n"); if (perfTimeNow >= mSeekTime) { PROGRESS("VidConsumer::DisplayThread - DO SEEK\n"); mSeeking = false; mDeltaTime = mMediaTime; continue; } } // if we received a Start, deal with it if (mStarting) { PROGRESS("BBt848Controllable::CaptureRun mStartTime = %.4f TimeNow = %.4f\n", (double)mStartTime/M1, (double)perfTimeNow/M1); if (perfTimeNow >= mStartTime) { mRunning = true; mStarting = false; mDeltaTime = mStartTime; //if (mConnected) // SendDataStatus(B_DATA_AVAILABLE, mConnections[0], mStartTime); continue; } } if (mRunning) { // check for buffer available. status_t err = acquire_sem_etc(mBufferAvailable, 1, B_TIMEOUT, halfPeriod * 2); if (err == B_TIMED_OUT || !mConnected) { ERROR("VidConsumer::DisplayThread - Error from acquire_sem_etc: 0x%lx\n", err); continue; } BBuffer* buffer = mBufferQueue->PopFirstBuffer(0); LOOP("Popped buffer %08x, Start time: %.4f, system time: %.4f diff: %.4f\n", buffer, (double) buffer->Header()->start_time/M1, (double) perfTimeNow/M1, (double) (buffer->Header()->start_time - perfTimeNow)/M1); // Display frame if we're in B_OFFLINE mode or // within +/- a half frame time of start time if ( (mRunMode == B_OFFLINE) || ((perfTimeNow > (buffer->Header()->start_time - halfPeriod)) && (perfTimeNow < (buffer->Header()->start_time + halfPeriod))) ) { uint32 bpp = (mColorspace == B_RGB32 ? 4 : 2); memcpy(m_Bitmap->Bits(), buffer->Data(), mRowBytes * mYSize * bpp); buffer->Header()->start_time = system_time(); buffer->Recycle(); bigtime_t t1 = system_time(); // Update view if (LockLooper()) { DrawBitmap(m_Bitmap, Bounds()); UnlockLooper(); } t1 = system_time() - t1; if (t1/M1 > .030) printf("Draw time = %.4f\n",t1/M1); continue; } else { // If we're too early, push frame back on stack if (perfTimeNow < buffer->Header()->start_time) { LOOP("push buffer back on stack!\n"); mBufferQueue->PushBuffer(buffer, buffer->Header()->start_time); release_sem(mBufferAvailable); continue; } else { // if we've already passed a half frame time past the buffer start time // and RunMode = INCREASE_LATENCY, increase latency and display the frame if ( (perfTimeNow > buffer->Header()->start_time) && (mRunMode == B_INCREASE_LATENCY)) { mMyLatency += halfPeriod; ERROR("VidConsumer::DisplayThread - Increased latency to: %.4f\n", mMyLatency); ERROR(" Performance time: %.4f @ %.4f\n", (double)buffer->Header()->start_time/M1, (double)perfTimeNow/M1); uint32 bpp = (mColorspace == B_RGB32 ? 4 : 2); memcpy(m_Bitmap->Bits(), buffer->Data(), mRowBytes * mYSize * bpp); buffer->Recycle(); // should send late notice if (LockLooper()) { DrawBitmap(m_Bitmap, Bounds()); UnlockLooper(); } continue; } else { // we're more than a half frame time past the buffer start time // drop the frame ERROR("VidConsumer::DisplayThread - dropped late frame: %.4f @ %.4f\n", (double)buffer->Header()->start_time/M1, (double)perfTimeNow/M1); buffer->Recycle(); // should send late notice continue; } } } } snooze(timeout); } else snooze(timeout); // if TimeSource stopped } // while (!mTimeToQuit) }
int main() { // app_server connection (no need to run it) BApplication app("application/x-vnd-test"); BBufferGroup * group; status_t s; int32 count; BBuffer *buffer; /* printf("using default constructor:\n"); group = new BBufferGroup(); s = group->InitCheck(); printf("InitCheck: status = %ld\n",s); s = group->CountBuffers(&count); printf("CountBuffers: count = %ld, status = %ld\n",count,s); delete group; */ printf("\n"); printf("using size = 1234 constructor:\n"); group = new BBufferGroup(1234); s = group->InitCheck(); printf("InitCheck: status = %ld\n",s); s = group->CountBuffers(&count); printf("CountBuffers: count = %ld, status = %ld\n",count,s); s = group->GetBufferList(1,&buffer); printf("GetBufferList: status = %ld\n",s); printf("Buffer->Data: = %08x\n",(int)buffer->Data()); printf("Buffer->ID: = %d\n",(int)buffer->ID()); printf("Buffer->Size: = %ld\n",buffer->Size()); printf("Buffer->SizeAvailable: = %ld\n",buffer->SizeAvailable()); printf("Buffer->SizeUsed: = %ld\n",buffer->SizeUsed()); printf("\n"); media_buffer_id id = buffer->ID(); BBufferGroup * group2 = new BBufferGroup(1,&id); printf("creating second group with a buffer from first group:\n"); s = group2->InitCheck(); printf("InitCheck: status = %ld\n",s); s = group2->CountBuffers(&count); printf("CountBuffers: count = %ld, status = %ld\n",count,s); buffer = 0; s = group2->GetBufferList(1,&buffer); printf("GetBufferList: status = %ld\n",s); printf("Buffer->Data: = %08x\n",(int)buffer->Data()); printf("Buffer->ID: = %d\n",(int)buffer->ID()); printf("Buffer->Size: = %ld\n",buffer->Size()); printf("Buffer->SizeAvailable: = %ld\n",buffer->SizeAvailable()); printf("Buffer->SizeUsed: = %ld\n",buffer->SizeUsed()); delete group; delete group2; printf("\n"); /* printf("creating a BSmallBuffer:\n"); BSmallBuffer * sb = new BSmallBuffer; printf("sb->Data: = %08x\n",(int)sb->Data()); printf("sb->ID: = %d\n",(int)sb->ID()); printf("sb->Size: = %ld\n",sb->Size()); printf("sb->SizeAvailable: = %ld\n",sb->SizeAvailable()); printf("sb->SizeUsed: = %ld\n",sb->SizeUsed()); printf("sb->SmallBufferSizeLimit: = %ld\n",sb->SmallBufferSizeLimit()); delete sb; */ return 0; }
void AudioProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) { TRACE_BUFFER("%p->AudioProducer::HandleEvent()\n", this); switch (event->type) { case BTimedEventQueue::B_START: TRACE("AudioProducer::HandleEvent(B_START)\n"); if (RunState() != B_STARTED) { fFramesSent = 0; fStartTime = event->event_time + fSupplier->InitialLatency(); printf("B_START: start time: %lld\n", fStartTime); media_timed_event firstBufferEvent( fStartTime - fSupplier->InitialLatency(), BTimedEventQueue::B_HANDLE_BUFFER); EventQueue()->AddEvent(firstBufferEvent); } TRACE("AudioProducer::HandleEvent(B_START) done\n"); break; case BTimedEventQueue::B_STOP: TRACE("AudioProducer::HandleEvent(B_STOP)\n"); EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); TRACE("AudioProducer::HandleEvent(B_STOP) done\n"); break; case BTimedEventQueue::B_HANDLE_BUFFER: { TRACE_BUFFER("AudioProducer::HandleEvent(B_HANDLE_BUFFER)\n"); if (RunState() == BMediaEventLooper::B_STARTED && fOutput.destination != media_destination::null) { BBuffer* buffer = _FillNextBuffer(event->event_time); if (buffer) { status_t err = B_ERROR; if (fOutputEnabled) { err = SendBuffer(buffer, fOutput.source, fOutput.destination); } if (err) buffer->Recycle(); } size_t sampleSize = fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK; size_t nFrames = fOutput.format.u.raw_audio.buffer_size / (sampleSize * fOutput.format.u.raw_audio.channel_count); fFramesSent += nFrames; fNextScheduledBuffer = fStartTime + bigtime_t(double(fFramesSent) * 1000000.0 / double(fOutput.format.u.raw_audio.frame_rate)); media_timed_event nextBufferEvent(fNextScheduledBuffer, BTimedEventQueue::B_HANDLE_BUFFER); EventQueue()->AddEvent(nextBufferEvent); } else { ERROR("B_HANDLE_BUFFER, but not started!\n"); } TRACE_BUFFER("AudioProducer::HandleEvent(B_HANDLE_BUFFER) done\n"); break; } default: break; } }
void GameProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) { // FPRINTF(stderr, "ToneProducer::HandleEvent\n"); switch (event->type) { case BTimedEventQueue::B_START: // don't do anything if we're already running if (RunState() != B_STARTED) { // Going to start sending buffers so setup the needed bookkeeping fFramesSent = 0; fStartTime = event->event_time; media_timed_event firstBufferEvent(fStartTime, BTimedEventQueue::B_HANDLE_BUFFER); // Alternatively, we could call HandleEvent() directly with this // event, to avoid a trip through the event queue like this: // this->HandleEvent(&firstBufferEvent, 0, false); EventQueue()->AddEvent(firstBufferEvent); } break; case BTimedEventQueue::B_STOP: // When we handle a stop, we must ensure that downstream consumers don't // get any more buffers from us. This means we have to flush any // pending buffer-producing events from the queue. EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); break; case BTimedEventQueue::B_HANDLE_BUFFER: { // Ensure we're both started and connected before delivering buffer if ((RunState() == BMediaEventLooper::B_STARTED) && (fOutput.destination != media_destination::null)) { // Get the next buffer of data BBuffer* buffer = FillNextBuffer(event->event_time); if (buffer) { // Send the buffer downstream if output is enabled status_t err = B_ERROR; if (fOutputEnabled) { err = SendBuffer(buffer, fOutput.source, fOutput.destination); } if (err) { // we need to recycle the buffer ourselves if output is // disabled or if the call to SendBuffer() fails buffer->Recycle(); } } // track how much media we've delivered so far size_t nFrames = fBufferSize / fFrameSize; fFramesSent += nFrames; // The buffer is on its way; now schedule the next one to go bigtime_t nextEvent = fStartTime + bigtime_t(double(fFramesSent) / double(fOutput.format.u.raw_audio.frame_rate) * 1000000.0); media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER); EventQueue()->AddEvent(nextBufferEvent); } } break; default: break; } }
// how should we handle late buffers? drop them? // notify the producer? status_t SoundPlayNode::SendNewBuffer(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) { CALLED(); // printf("latency = %12Ld, event = %12Ld, sched = %5Ld, arrive at %12Ld, now %12Ld, current lateness %12Ld\n", EventLatency() + SchedulingLatency(), EventLatency(), SchedulingLatency(), event->event_time, TimeSource()->Now(), lateness); // make sure we're both started *and* connected before delivering a buffer if (RunState() != BMediaEventLooper::B_STARTED || fOutput.destination == media_destination::null) return B_OK; // The event->event_time is the time at which the buffer we are preparing // here should arrive at it's destination. The MediaEventLooper should have // scheduled us early enough (based on EventLatency() and the // SchedulingLatency()) to make this possible. // lateness is independent of EventLatency()! if (lateness > (BufferDuration() / 3) ) { printf("SoundPlayNode::SendNewBuffer, event scheduled much too late, " "lateness is %Ld\n", lateness); } // skip buffer creation if output not enabled if (fOutputEnabled) { // Get the next buffer of data BBuffer* buffer = FillNextBuffer(event->event_time); if (buffer) { // If we are ready way too early, decrase internal latency /* bigtime_t how_early = event->event_time - TimeSource()->Now() - fLatency - fInternalLatency; if (how_early > 5000) { printf("SoundPlayNode::SendNewBuffer, event scheduled too early, how_early is %Ld\n", how_early); if (fTooEarlyCount++ == 5) { fInternalLatency -= how_early; if (fInternalLatency < 500) fInternalLatency = 500; printf("SoundPlayNode::SendNewBuffer setting internal latency to %Ld\n", fInternalLatency); SetEventLatency(fLatency + fInternalLatency); fTooEarlyCount = 0; } } */ // send the buffer downstream if and only if output is enabled if (SendBuffer(buffer, fOutput.source, fOutput.destination) != B_OK) { // we need to recycle the buffer // if the call to SendBuffer() fails printf("SoundPlayNode::SendNewBuffer: Buffer sending " "failed\n"); buffer->Recycle(); } } } // track how much media we've delivered so far size_t nFrames = fOutput.format.u.raw_audio.buffer_size / ((fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) * fOutput.format.u.raw_audio.channel_count); fFramesSent += nFrames; // The buffer is on its way; now schedule the next one to go // nextEvent is the time at which the buffer should arrive at it's // destination bigtime_t nextEvent = fStartTime + bigtime_t((1000000LL * fFramesSent) / (int32)fOutput.format.u.raw_audio.frame_rate); media_timed_event nextBufferEvent(nextEvent, SEND_NEW_BUFFER_EVENT); EventQueue()->AddEvent(nextBufferEvent); return B_OK; }
status_t BBufferConsumer::HandleMessage(int32 message, const void* data, size_t size) { PRINT(4, "BBufferConsumer::HandleMessage %#lx, node %ld\n", message, ID()); status_t rv; switch (message) { case CONSUMER_ACCEPT_FORMAT: { const consumer_accept_format_request* request = static_cast<const consumer_accept_format_request*>(data); consumer_accept_format_reply reply; reply.format = request->format; status_t status = AcceptFormat(request->dest, &reply.format); request->SendReply(status, &reply, sizeof(reply)); return B_OK; } case CONSUMER_GET_NEXT_INPUT: { const consumer_get_next_input_request *request = static_cast<const consumer_get_next_input_request *>(data); consumer_get_next_input_reply reply; reply.cookie = request->cookie; rv = GetNextInput(&reply.cookie, &reply.input); request->SendReply(rv, &reply, sizeof(reply)); return B_OK; } case CONSUMER_DISPOSE_INPUT_COOKIE: { const consumer_dispose_input_cookie_request *request = static_cast<const consumer_dispose_input_cookie_request *>(data); consumer_dispose_input_cookie_reply reply; DisposeInputCookie(request->cookie); request->SendReply(B_OK, &reply, sizeof(reply)); return B_OK; } case CONSUMER_BUFFER_RECEIVED: { const consumer_buffer_received_command* command = static_cast<const consumer_buffer_received_command*>(data); BBuffer* buffer = fBufferCache->GetBuffer(command->buffer); if (buffer == NULL) { ERROR("BBufferConsumer::CONSUMER_BUFFER_RECEIVED can't" "find the buffer\n"); } else { buffer->SetHeader(&command->header); PRINT(4, "calling BBufferConsumer::BufferReceived buffer %ld " "at perf %Ld and TimeSource()->Now() is %Ld\n", buffer->Header()->buffer, buffer->Header()->start_time, TimeSource()->Now()); BufferReceived(buffer); } return B_OK; } case CONSUMER_PRODUCER_DATA_STATUS: { const consumer_producer_data_status_command *command = static_cast<const consumer_producer_data_status_command *>(data); ProducerDataStatus(command->for_whom, command->status, command->at_performance_time); return B_OK; } case CONSUMER_GET_LATENCY_FOR: { const consumer_get_latency_for_request *request = static_cast<const consumer_get_latency_for_request *>(data); consumer_get_latency_for_reply reply; rv = GetLatencyFor(request->for_whom, &reply.latency, &reply.timesource); request->SendReply(rv, &reply, sizeof(reply)); return B_OK; } case CONSUMER_CONNECTED: { const consumer_connected_request *request = static_cast<const consumer_connected_request *>(data); consumer_connected_reply reply; reply.input = request->input; rv = Connected(request->input.source, request->input.destination, request->input.format, &reply.input); request->SendReply(rv, &reply, sizeof(reply)); return B_OK; } case CONSUMER_DISCONNECTED: { const consumer_disconnected_request *request = static_cast<const consumer_disconnected_request *>(data); consumer_disconnected_reply reply; Disconnected(request->source, request->destination); request->SendReply(B_OK, &reply, sizeof(reply)); return B_OK; } case CONSUMER_FORMAT_CHANGED: { const consumer_format_changed_request *request = static_cast<const consumer_format_changed_request *>(data); consumer_format_changed_reply reply; rv = FormatChanged(request->producer, request->consumer, request->change_tag, request->format); request->SendReply(rv, &reply, sizeof(reply)); // XXX is this RequestCompleted() correct? node_request_completed_command completedcommand; completedcommand.info.what = media_request_info::B_FORMAT_CHANGED; completedcommand.info.change_tag = request->change_tag; completedcommand.info.status = reply.result; //completedcommand.info.cookie completedcommand.info.user_data = 0; completedcommand.info.source = request->producer; completedcommand.info.destination = request->consumer; completedcommand.info.format = request->format; SendToPort(request->consumer.port, NODE_REQUEST_COMPLETED, &completedcommand, sizeof(completedcommand)); return B_OK; } case CONSUMER_SEEK_TAG_REQUESTED: { const consumer_seek_tag_requested_request *request = static_cast<const consumer_seek_tag_requested_request *>(data); consumer_seek_tag_requested_reply reply; rv = SeekTagRequested(request->destination, request->target_time, request->flags, &reply.seek_tag, &reply.tagged_time, &reply.flags); request->SendReply(rv, &reply, sizeof(reply)); return B_OK; } } return B_ERROR; }
void MediaReader::Connect( status_t error, const media_source & source, const media_destination & destination, const media_format & format, char * io_name) { CALLED(); if (error != B_OK) { PRINT("\t<- error already\n"); output.destination = media_destination::null; GetFormat(&output.format); return; } if (output.source != source) { PRINT("\t<- B_MEDIA_BAD_SOURCE\n"); output.destination = media_destination::null; GetFormat(&output.format); return; } // record the agreed upon values output.destination = destination; output.format = format; strncpy(io_name,output.name,B_MEDIA_NAME_LENGTH-1); io_name[B_MEDIA_NAME_LENGTH-1] = '\0'; // determine our downstream latency media_node_id id; FindLatencyFor(output.destination, &fDownstreamLatency, &id); // compute the buffer period (must be done before setbuffergroup) fBufferPeriod = bigtime_t(1000 * 8000000 / 1024 * output.format.u.multistream.max_chunk_size / output.format.u.multistream.max_bit_rate); PRINT("\tmax chunk size = %ld, max bit rate = %f, buffer period = %lld\n", output.format.u.multistream.max_chunk_size, output.format.u.multistream.max_bit_rate,fBufferPeriod); // setup the buffers if they aren't setup yet if (fBufferGroup == 0) { status_t status = SetBufferGroup(output.source,0); if (status != B_OK) { PRINT("\t<- SetBufferGroup failed\n"); output.destination = media_destination::null; GetFormat(&output.format); return; } } SetBufferDuration(fBufferPeriod); if (GetCurrentFile() != 0) { bigtime_t start, end; // buffer group buffer size uint8 * data = new uint8[output.format.u.multistream.max_chunk_size]; BBuffer * buffer = 0; ssize_t bytesRead = 0; { // timed section start = TimeSource()->RealTime(); // first we try to use a real BBuffer buffer = fBufferGroup->RequestBuffer( output.format.u.multistream.max_chunk_size,fBufferPeriod); if (buffer != 0) { FillFileBuffer(buffer); } else { // didn't get a real BBuffer, try simulation by just a read from the disk bytesRead = GetCurrentFile()->Read( data, output.format.u.multistream.max_chunk_size); } end = TimeSource()->RealTime(); } bytesRead = buffer->SizeUsed(); delete data; if (buffer != 0) { buffer->Recycle(); } GetCurrentFile()->Seek(-bytesRead,SEEK_CUR); // put it back where we found it fInternalLatency = end - start; PRINT("\tinternal latency from disk read = %lld\n", fInternalLatency); } else { fInternalLatency = 100; // just guess PRINT("\tinternal latency guessed = %lld\n", fInternalLatency); } SetEventLatency(fDownstreamLatency + fInternalLatency); // XXX: do anything else? }
int32 VideoProducer::_FrameGeneratorThread() { bool forceSendingBuffer = true; int32 droppedFrames = 0; const int32 kMaxDroppedFrames = 15; bool running = true; while (running) { TRACE("_FrameGeneratorThread: loop: %Ld\n", fFrame); // lock the node manager status_t err = fManager->LockWithTimeout(10000); bool ignoreEvent = false; // Data to be retrieved from the node manager. bigtime_t performanceTime = 0; bigtime_t nextPerformanceTime = 0; bigtime_t waitUntil = 0; bigtime_t nextWaitUntil = 0; int32 playingDirection = 0; int32 playingMode = 0; int64 playlistFrame = 0; switch (err) { case B_OK: { TRACE("_FrameGeneratorThread: node manager successfully " "locked\n"); if (droppedFrames > 0) fManager->FrameDropped(); // get the times for the current and the next frame performanceTime = fManager->TimeForFrame(fFrame); nextPerformanceTime = fManager->TimeForFrame(fFrame + 1); playingMode = fManager->PlayModeAtFrame(fFrame); waitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase + performanceTime, fBufferLatency); nextWaitUntil = TimeSource()->RealTimeFor(fPerformanceTimeBase + nextPerformanceTime, fBufferLatency); // get playing direction and playlist frame for the current // frame bool newPlayingState; playlistFrame = fManager->PlaylistFrameAtFrame(fFrame, playingDirection, newPlayingState); TRACE("_FrameGeneratorThread: performance time: %Ld, " "playlist frame: %lld\n", performanceTime, playlistFrame); forceSendingBuffer |= newPlayingState; fManager->SetCurrentVideoTime(nextPerformanceTime); fManager->Unlock(); break; } case B_TIMED_OUT: TRACE("_FrameGeneratorThread: Couldn't lock the node " "manager.\n"); ignoreEvent = true; waitUntil = system_time() - 1; break; default: ERROR("_FrameGeneratorThread: Couldn't lock the node manager. " "Terminating video producer frame generator thread.\n"); TRACE("_FrameGeneratorThread: frame generator thread done.\n"); // do not access any member variables, since this could // also mean the Node has been deleted return B_OK; } TRACE("_FrameGeneratorThread: waiting (%Ld)...\n", waitUntil); // wait until... err = acquire_sem_etc(fFrameSync, 1, B_ABSOLUTE_TIMEOUT, waitUntil); // The only acceptable responses are B_OK and B_TIMED_OUT. Everything // else means the thread should quit. Deleting the semaphore, as in // VideoProducer::_HandleStop(), will trigger this behavior. switch (err) { case B_OK: TRACE("_FrameGeneratorThread: going back to sleep.\n"); break; case B_TIMED_OUT: TRACE("_FrameGeneratorThread: timed out => event\n"); // Catch the cases in which the node manager could not be // locked and we therefore have no valid data to work with, // or the producer is not running or enabled. if (ignoreEvent || !fRunning || !fEnabled) { TRACE("_FrameGeneratorThread: ignore event\n"); // nothing to do } else if (!forceSendingBuffer && nextWaitUntil < system_time() - fBufferLatency && droppedFrames < kMaxDroppedFrames) { // Drop frame if it's at least a frame late. if (playingDirection > 0) printf("VideoProducer: dropped frame (%Ld)\n", fFrame); // next frame droppedFrames++; fFrame++; } else if (playingDirection != 0 || forceSendingBuffer) { // Send buffers only, if playing, the node is running and // the output has been enabled TRACE("_FrameGeneratorThread: produce frame\n"); BAutolock _(fLock); // Fetch a buffer from the buffer group fUsedBufferGroup->WaitForBuffers(); BBuffer* buffer = fUsedBufferGroup->RequestBuffer( fConnectedFormat.display.bytes_per_row * fConnectedFormat.display.line_count, 0LL); if (buffer == NULL) { // Wait until a buffer becomes available again ERROR("_FrameGeneratorThread: no buffer!\n"); break; } // Fill out the details about this buffer. media_header* h = buffer->Header(); h->type = B_MEDIA_RAW_VIDEO; h->time_source = TimeSource()->ID(); h->size_used = fConnectedFormat.display.bytes_per_row * fConnectedFormat.display.line_count; // For a buffer originating from a device, you might // want to calculate this based on the // PerformanceTimeFor the time your buffer arrived at // the hardware (plus any applicable adjustments). h->start_time = fPerformanceTimeBase + performanceTime; h->file_pos = 0; h->orig_size = 0; h->data_offset = 0; h->u.raw_video.field_gamma = 1.0; h->u.raw_video.field_sequence = fFrame; h->u.raw_video.field_number = 0; h->u.raw_video.pulldown_number = 0; h->u.raw_video.first_active_line = 1; h->u.raw_video.line_count = fConnectedFormat.display.line_count; // Fill in a frame TRACE("_FrameGeneratorThread: frame: %Ld, " "playlistFrame: %Ld\n", fFrame, playlistFrame); bool wasCached = false; err = fSupplier->FillBuffer(playlistFrame, buffer->Data(), fConnectedFormat, forceSendingBuffer, wasCached); if (err == B_TIMED_OUT) { // Don't send the buffer if there was insufficient // time for rendering, this will leave the last // valid frame on screen until we catch up, instead // of going black. wasCached = true; err = B_OK; } // clean the buffer if something went wrong if (err != B_OK) { // TODO: should use "back value" according // to color space! memset(buffer->Data(), 0, h->size_used); err = B_OK; } // Send the buffer on down to the consumer if (wasCached || (err = SendBuffer(buffer, fOutput.source, fOutput.destination) != B_OK)) { // If there is a problem sending the buffer, // or if we don't send the buffer because its // contents are the same as the last one, // return it to its buffer group. buffer->Recycle(); // we tell the supplier to delete // its caches if there was a problem sending // the buffer if (err != B_OK) { ERROR("_FrameGeneratorThread: Error " "sending buffer\n"); fSupplier->DeleteCaches(); } } // Only if everything went fine we clear the flag // that forces us to send a buffer even if not // playing. if (err == B_OK) forceSendingBuffer = false; // next frame fFrame++; droppedFrames = 0; } else { TRACE("_FrameGeneratorThread: not playing\n"); // next frame fFrame++; } break; default: TRACE("_FrameGeneratorThread: Couldn't acquire semaphore. " "Error: %s\n", strerror(err)); running = false; break; } } TRACE("_FrameGeneratorThread: frame generator thread done.\n"); return B_OK; }
BBuffer* ToneProducer::FillNextBuffer(bigtime_t event_time) { // get a buffer from our buffer group BBuffer* buf = mBufferGroup->RequestBuffer(mOutput.format.u.raw_audio.buffer_size, BufferDuration()); // if we fail to get a buffer (for example, if the request times out), we skip this // buffer and go on to the next, to avoid locking up the control thread if (!buf) { return NULL; } // now fill it with data, continuing where the last buffer left off // 20sep99: multichannel support size_t numFrames = mOutput.format.u.raw_audio.buffer_size / (sizeof(float)*mOutput.format.u.raw_audio.channel_count); bool stereo = (mOutput.format.u.raw_audio.channel_count == 2); if(!stereo) { ASSERT(mOutput.format.u.raw_audio.channel_count == 1); } // PRINT(("buffer: %ld, %ld frames, %s\n", mOutput.format.u.raw_audio.buffer_size, numFrames, stereo ? "stereo" : "mono")); float* data = (float*) buf->Data(); switch (mWaveform) { case SINE_WAVE: FillSineBuffer(data, numFrames, stereo); break; case TRIANGLE_WAVE: FillTriangleBuffer(data, numFrames, stereo); break; case SAWTOOTH_WAVE: FillSawtoothBuffer(data, numFrames, stereo); break; } // fill in the buffer header media_header* hdr = buf->Header(); hdr->type = B_MEDIA_RAW_AUDIO; hdr->size_used = mOutput.format.u.raw_audio.buffer_size; hdr->time_source = TimeSource()->ID(); bigtime_t stamp; if (RunMode() == B_RECORDING) { // In B_RECORDING mode, we stamp with the capture time. We're not // really a hardware capture node, but we simulate it by using the (precalculated) // time at which this buffer "should" have been created. stamp = event_time; } else { // okay, we're in one of the "live" performance run modes. in these modes, we // stamp the buffer with the time at which the buffer should be rendered to the // output, not with the capture time. mStartTime is the cached value of the // first buffer's performance time; we calculate this buffer's performance time as // an offset from that time, based on the amount of media we've created so far. // Recalculating every buffer like this avoids accumulation of error. stamp = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); } hdr->start_time = stamp; return buf; }
void ToneProducer::HandleEvent(const media_timed_event* event, bigtime_t lateness, bool realTimeEvent) { // FPRINTF(stderr, "ToneProducer::HandleEvent\n"); switch (event->type) { case BTimedEventQueue::B_START: // don't do anything if we're already running if (RunState() != B_STARTED) { // We want to start sending buffers now, so we set up the buffer-sending bookkeeping // and fire off the first "produce a buffer" event. mFramesSent = 0; mTheta = 0; mStartTime = event->event_time; media_timed_event firstBufferEvent(mStartTime, BTimedEventQueue::B_HANDLE_BUFFER); // Alternatively, we could call HandleEvent() directly with this event, to avoid a trip through // the event queue, like this: // // this->HandleEvent(&firstBufferEvent, 0, false); // EventQueue()->AddEvent(firstBufferEvent); } break; case BTimedEventQueue::B_STOP: FPRINTF(stderr, "Handling B_STOP event\n"); // When we handle a stop, we must ensure that downstream consumers don't // get any more buffers from us. This means we have to flush any pending // buffer-producing events from the queue. EventQueue()->FlushEvents(0, BTimedEventQueue::B_ALWAYS, true, BTimedEventQueue::B_HANDLE_BUFFER); break; case _PARAMETER_EVENT: { size_t dataSize = size_t(event->data); int32 param = int32(event->bigdata); if (dataSize >= sizeof(float)) switch (param) { case FREQUENCY_PARAM: { float newValue = *((float*) event->user_data); if (mFrequency != newValue) // an actual change in the value? { mFrequency = newValue; mFreqLastChanged = TimeSource()->Now(); BroadcastNewParameterValue(mFreqLastChanged, param, &mFrequency, sizeof(mFrequency)); } } break; case GAIN_PARAM: { float newValue = *((float*) event->user_data); if (mGain != newValue) { mGain = newValue; mGainLastChanged = TimeSource()->Now(); BroadcastNewParameterValue(mGainLastChanged, param, &mGain, sizeof(mGain)); } } break; case WAVEFORM_PARAM: { int32 newValue = *((int32*) event->user_data); if (mWaveform != newValue) { mWaveform = newValue; mTheta = 0; // reset the generator parameters when we change waveforms mWaveAscending = true; mWaveLastChanged = TimeSource()->Now(); BroadcastNewParameterValue(mWaveLastChanged, param, &mWaveform, sizeof(mWaveform)); } } break; default: FPRINTF(stderr, "Hmmm... got a B_PARAMETER event for a parameter we don't have? (%" B_PRId32 ")\n", param); break; } } break; case BTimedEventQueue::B_HANDLE_BUFFER: { // make sure we're both started *and* connected before delivering a buffer if (RunState() == BMediaEventLooper::B_STARTED && mOutput.destination != media_destination::null) { // Get the next buffer of data BBuffer* buffer = FillNextBuffer(event->event_time); if (buffer) { // send the buffer downstream if and only if output is enabled status_t err = B_ERROR; if (mOutputEnabled) { err = SendBuffer(buffer, mOutput.source, mOutput.destination); } if (err) { // we need to recycle the buffer ourselves if output is disabled or // if the call to SendBuffer() fails buffer->Recycle(); } } // track how much media we've delivered so far size_t nFrames = mOutput.format.u.raw_audio.buffer_size / (sizeof(float) * mOutput.format.u.raw_audio.channel_count); mFramesSent += nFrames; // The buffer is on its way; now schedule the next one to go bigtime_t nextEvent = mStartTime + bigtime_t(double(mFramesSent) / double(mOutput.format.u.raw_audio.frame_rate) * 1000000.0); media_timed_event nextBufferEvent(nextEvent, BTimedEventQueue::B_HANDLE_BUFFER); EventQueue()->AddEvent(nextBufferEvent); } } break; default: 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); }
void AudioFilterNode::BufferReceived( BBuffer* buffer) { ASSERT(buffer); // check buffer destination if(buffer->Header()->destination != m_input.destination.id) { PRINT(("AudioFilterNode::BufferReceived():\n" "\tBad destination.\n")); buffer->Recycle(); return; } if(buffer->Header()->time_source != TimeSource()->ID()) { // +++++ no-go in offline mode PRINT(("* timesource mismatch\n")); } // check output if(m_output.destination == media_destination::null || !m_outputEnabled) { buffer->Recycle(); return; } // // +++++ [9sep99] // bigtime_t now = TimeSource()->Now(); // bigtime_t delta = now - m_tpLastReceived; // m_tpLastReceived = now; // PRINT(( // "### delta: %Ld (%Ld)\n", // delta, buffer->Header()->start_time - now)); // fetch outbound buffer if needed BBuffer* outBuffer; if(m_bufferGroup) { outBuffer = m_bufferGroup->RequestBuffer( m_output.format.u.raw_audio.buffer_size, -1); ASSERT(outBuffer); // prepare outbound buffer outBuffer->Header()->type = B_MEDIA_RAW_AUDIO; // copy start time info from upstream node // +++++ is this proper, or should the next buffer-start be // continuously tracked (figured from Start() or the first // buffer received?) outBuffer->Header()->time_source = buffer->Header()->time_source; outBuffer->Header()->start_time = buffer->Header()->start_time; } else { // process inplace outBuffer = buffer; } // process and retransmit buffer processBuffer(buffer, outBuffer); status_t err = SendBuffer(outBuffer, m_output.source, m_output.destination); if (err < B_OK) { PRINT(("AudioFilterNode::BufferReceived():\n" "\tSendBuffer() failed: %s\n", strerror(err))); outBuffer->Recycle(); } // free inbound buffer if data was copied if(buffer != outBuffer) buffer->Recycle(); // //####resend // SendBuffer(buffer, m_output.destination); // sent! }