void TimeComputer::AddTimeStamp(bigtime_t realTime, uint64 frames) { bigtime_t estimatedPerformanceTime = fPerformanceTime + bigtime_t((realTime - fRealTime) * fDrift); fRealTime = realTime; if (fResetTimeBase) { // use the extrapolated performance time at the given real time fPerformanceTime = estimatedPerformanceTime; fPerformanceTimeBase = estimatedPerformanceTime; fFrameBase = frames; fResetTimeBase = false; _AddEntry(fRealTime, fPerformanceTime); return; } // add entry bigtime_t performanceTime = fPerformanceTimeBase + bigtime_t((frames - fFrameBase) * fUsecsPerFrame); _AddEntry(realTime, performanceTime); // Update performance time and drift. We don't use the given // performance time directly, but average it with the estimated // performance time. fPerformanceTime = (performanceTime + estimatedPerformanceTime) / 2; Entry& entry = fEntries[fFirstEntry]; fDrift = double(fPerformanceTime - entry.performanceTime) / double(fRealTime - entry.realTime); }
status_t AVFormatReader::GetMetaData(BMessage* _data) { // The first cookie is always there! const AVFormatContext* context = fStreams[0]->Context(); if (context == NULL) return B_NO_INIT; avdictionary_to_message(context->metadata, _data); // Add chapter info for (unsigned i = 0; i < context->nb_chapters; i++) { AVChapter* chapter = context->chapters[i]; BMessage chapterData; chapterData.AddInt64("start", bigtime_t(1000000.0 * chapter->start * chapter->time_base.num / chapter->time_base.den + 0.5)); chapterData.AddInt64("end", bigtime_t(1000000.0 * chapter->end * chapter->time_base.num / chapter->time_base.den + 0.5)); avdictionary_to_message(chapter->metadata, &chapterData); _data->AddMessage("be:chapter", &chapterData); } // Add program info for (unsigned i = 0; i < context->nb_programs; i++) { BMessage programData; avdictionary_to_message(context->programs[i]->metadata, &programData); _data->AddMessage("be:program", &programData); } return B_OK; }
status_t GameSoundBuffer::GetAttributes(gs_attribute * attributes, size_t attributeCount) { for (size_t i = 0; i < attributeCount; i++) { switch (attributes[i].attribute) { case B_GS_GAIN: attributes[i].value = fGain; if (fGainRamp) attributes[i].duration = fGainRamp->duration; break; case B_GS_PAN: attributes[i].value = fPan; if (fPanRamp) attributes[i].duration = fPanRamp->duration; break; case B_GS_LOOPING: attributes[i].value = (fLooping) ? -1.0 : 0.0; attributes[i].duration = bigtime_t(0); break; default: attributes[i].value = 0.0; attributes[i].duration = bigtime_t(0); break; } } return B_OK; }
void GameProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) { // If something earlier failed, Connect() might still be called, but with a non-zero // error code. When that happens we simply unreserve the connection and do // nothing else. if (error) { fOutput.destination = media_destination::null; fOutput.format = fPreferredFormat; return; } // Okay, the connection has been confirmed. Record the destination and format // that we agreed on, and report our connection name again. fOutput.destination = destination; fOutput.format = format; strlcpy(io_name, fOutput.name, B_MEDIA_NAME_LENGTH); // Now that we're connected, we can determine our downstream latency. // Do so, then make sure we get our events early enough. media_node_id id; FindLatencyFor(fOutput.destination, &fLatency, &id); if (!fBufferGroup) fBufferSize = fOutput.format.u.raw_audio.buffer_size; // Have to set it before latency calculating // Use a dry run to see how long it takes me to fill a buffer of data // The first step to setup the buffer bigtime_t start, produceLatency; int32 frames = int32(fBufferSize / fFrameSize); float* data = new float[frames * 2]; // Second, fill the buffer start = ::system_time(); for (int32 i = 0; i < frames; i++) { data[i*2] = 0.8 * float(i/frames); data[i*2+1] = 0.8 * float(i/frames); } produceLatency = ::system_time(); // Third, calculate the latency fInternalLatency = produceLatency - start; SetEventLatency(fLatency + fInternalLatency); // Finaily, clean up delete [] data; // reset our buffer duration, etc. to avoid later calculations bigtime_t duration = bigtime_t(1000000) * frames / bigtime_t(fOutput.format.u.raw_audio.frame_rate); SetBufferDuration(duration); // Set up the buffer group for our connection, as long as nobody handed us a // buffer group (via SetBufferGroup()) prior to this. if (!fBufferGroup) { int32 count = int32(fLatency / BufferDuration() + 2); fBufferGroup = new BBufferGroup(fBufferSize, count); } }
bigtime_t SoundPlayNode::CurrentTime() { int frameRate = (int)fOutput.format.u.raw_audio.frame_rate; return frameRate == 0 ? 0 : bigtime_t((1000000LL * fFramesSent) / frameRate); }
bigtime_t StreamBase::_ConvertFromStreamTimeBase(int64_t time) const { if (fStream->start_time != kNoPTSValue) time -= fStream->start_time; return bigtime_t(1000000.0 * time * fStream->time_base.num / fStream->time_base.den + 0.5); }
// returns result in # of microseconds bigtime_t MediaOutputInfo::ComputeBufferPeriod(const media_format & format) { bigtime_t bufferPeriod = 25*1000; // default 25 milliseconds switch (format.type) { case B_MEDIA_MULTISTREAM: // given a buffer size of 8192 bytes // and a bitrate of 1024 kilobits/millisecond (= 128 bytes/millisecond) // we need to produce a buffer every 64 milliseconds (= every 64000 microseconds) bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format) / format.u.multistream.max_bit_rate); break; case B_MEDIA_ENCODED_VIDEO: bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format) / format.u.encoded_video.max_bit_rate); break; case B_MEDIA_ENCODED_AUDIO: bufferPeriod = bigtime_t(1000.0 * 8.0 * ComputeBufferSize(format) / format.u.encoded_audio.bit_rate); break; case B_MEDIA_RAW_VIDEO: // Given a field rate of 50.00 fields per second, (PAL) // we need to generate a field/buffer // every 1/50 of a second = 20000 microseconds. bufferPeriod = bigtime_t(1000000.0 / format.u.raw_video.field_rate); break; case B_MEDIA_RAW_AUDIO: // Given a sample size of 4 bytes [B_AUDIO_INT] // and a channel count of 2 and a buffer_size // of 256 bytes and a frame_rate of 44100 Hertz (1/sec) // 1 frame = 1 sample/channel. // comes to ?? // this is a guess: bufferPeriod = bigtime_t(1000000.0 * ComputeBufferSize(format) / (format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK) / format.u.raw_audio.channel_count / format.u.raw_audio.frame_rate); break; default: break; } return bufferPeriod; }
status_t find_rel_time(const BMessage& msg, const char* name, int32 i, AmTime* timeVal) { double v; status_t res = msg.FindDouble(name, i, &v); if (res == B_OK) { *timeVal = bigtime_t(v*PPQN + .5); return res; } return find_time(msg, name, i, timeVal); }
// Win32 sNow() cannot be inlined due to bug in CW compiler. -ec 01.01.03 bigtime_t ZTicks::sNow() { uint64 frequency; ::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&frequency)); if (!frequency) { // A zero frequency indicates there's no performance counter support, // so we fall back to GetTickCount. return bigtime_t(::GetTickCount()) * 1000; } uint64 time; ::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&time)); return double(time) / double(frequency) * 1e6; }
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; }
status_t MusePackReader::GetStreamInfo(void *cookie, int64 *_frameCount, bigtime_t *_duration, media_format *format, const void **_infoBuffer, size_t *_infoSize) { if (cookie != NULL) return B_BAD_VALUE; *_frameCount = FRAMELEN * (int64)fInfo.simple.Frames; *_duration = bigtime_t(1000.0 * fInfo.simple.Frames * FRAMELEN / (fInfo.simple.SampleFreq / 1000.0) + 0.5); *format = fFormat; // we provide the stream info in this place *_infoBuffer = (void *)fDecoder; *_infoSize = sizeof(MPC_decoder); return B_OK; }
_gs_ramp* InitRamp(float* value, float set, float frames, bigtime_t duration) { float diff = (set > *value) ? set - *value : *value - set; bigtime_t sec = bigtime_t(duration / 1000000.0); float inc = diff * 200; _gs_ramp* ramp = new (std::nothrow) _gs_ramp; if (ramp != NULL) { ramp->value = value; ramp->frame_total = frames * sec; ramp->frame_inc = int(ramp->frame_total / inc); ramp->inc = (set - *value) / inc; ramp->frame_count = 0; ramp->frame_inc_count = 0; ramp->duration = duration; } return ramp; }
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; }
bigtime_t Clock::USec2Count(bigtime_t us) { return bigtime_t((us/1000000.0)*performanceFrequency); }
bigtime_t Clock::MSec2Count(double ms) { return bigtime_t((ms/1000)*performanceFrequency); }
void Clock::Set(float time_to) { startTime = Counter() - bigtime_t(time_to*performanceFrequency); }
/*! \brief Decodes next video frame. We decode exactly one video frame into fDecodedData. To achieve this goal, we might need to request several chunks of encoded data resulting in a variable execution time of this function. The length of the decoded video frame is stored in fDecodedDataSizeInBytes. If this variable is greater than zero, you can assert that there is a valid video frame available in fDecodedData. The decoded video frame in fDecodedData has color space conversion and deinterlacing already applied. To every decoded video frame there is a media_header populated in fHeader, containing the corresponding video frame properties. Normally every decoded video frame has a start_time field populated in the associated fHeader, that determines the presentation time of the frame. This relationship will only hold true, when each data chunk that is provided via GetNextChunk() contains data for exactly one encoded video frame (one complete frame) - not more and not less. We can decode data chunks that contain partial video frame data, too. In that case, you cannot trust the value of the start_time field in fHeader. We simply have no logic in place to establish a meaningful relationship between an incomplete frame and the start time it should be presented. Though this might change in the future. We can decode data chunks that contain more than one video frame, too. In that case, you cannot trust the value of the start_time field in fHeader. We simply have no logic in place to track the start_time across multiple video frames. So a meaningful relationship between the 2nd, 3rd, ... frame and the start time it should be presented isn't established at the moment. Though this might change in the future. More over the fOutputFrameRate variable is updated for every decoded video frame. On first call the member variables fSwsContext / fFormatConversionFunc are initialized. \returns B_OK when we successfully decoded one video frame \returns B_LAST_BUFFER_ERROR when there are no more video frames available. \returns B_NO_MEMORY when we have no memory left for correct operation. \returns Other Errors */ status_t AVCodecDecoder::_DecodeNextVideoFrame() { assert(fTempPacket.size >= 0); while (true) { status_t loadingChunkStatus = _LoadNextVideoChunkIfNeededAndAssignStartTime(); if (loadingChunkStatus == B_LAST_BUFFER_ERROR) return _FlushOneVideoFrameFromDecoderBuffer(); if (loadingChunkStatus != B_OK) { TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from " "GetNextChunk(): %s\n", strerror(err)); return loadingChunkStatus; } #if DO_PROFILING bigtime_t startTime = system_time(); #endif // NOTE: In the FFMPEG 0.10.2 code example decoding_encoding.c, the // length returned by avcodec_decode_video2() is used to update the // packet buffer size (here it is fTempPacket.size). This way the // packet buffer is allowed to contain incomplete frames so we are // required to buffer the packets between different calls to // _DecodeNextVideoFrame(). int gotVideoFrame = 0; int decodedDataSizeInBytes = avcodec_decode_video2(fContext, fRawDecodedPicture, &gotVideoFrame, &fTempPacket); if (decodedDataSizeInBytes < 0) { TRACE("[v] AVCodecDecoder: ignoring error in decoding frame %lld:" " %d\n", fFrame, len); // NOTE: An error from avcodec_decode_video2() is ignored by the // FFMPEG 0.10.2 example decoding_encoding.c. Only the packet // buffers are flushed accordingly fTempPacket.data = NULL; fTempPacket.size = 0; continue; } fTempPacket.size -= decodedDataSizeInBytes; fTempPacket.data += decodedDataSizeInBytes; bool gotNoVideoFrame = gotVideoFrame == 0; if (gotNoVideoFrame) { TRACE("frame %lld - no picture yet, decodedDataSizeInBytes: %d, " "chunk size: %ld\n", fFrame, decodedDataSizeInBytes, fChunkBufferSize); continue; } #if DO_PROFILING bigtime_t formatConversionStart = system_time(); #endif _HandleNewVideoFrameAndUpdateSystemState(); #if DO_PROFILING bigtime_t doneTime = system_time(); decodingTime += formatConversionStart - startTime; conversionTime += doneTime - formatConversionStart; profileCounter++; if (!(fFrame % 5)) { if (info) { printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required " "%Ld\n", decodingTime / profileCounter, conversionTime / profileCounter, fFrame, info->time_to_decode); } else { printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required " "%Ld\n", decodingTime / profileCounter, conversionTime / profileCounter, fFrame, bigtime_t(1000000LL / fOutputFrameRate)); } decodingTime = 0; conversionTime = 0; profileCounter = 0; } #endif 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 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; } }
/*! \brief Decode next video frame We decode exactly one video frame into fDecodedData. To achieve this goal, we might need to request several chunks of encoded data resulting in a variable execution time of this function. The length of the decoded video frame is stored in fDecodedDataSizeInBytes. If this variable is greater than zero, you can assert that there is a valid video frame available in fDecodedData. The decoded video frame in fDecodedData has color space conversion and deinterlacing already applied. To every decoded video frame there is a media_header populated in fHeader, containing the corresponding video frame properties. Normally every decoded video frame has a start_time field populated in the associated fHeader, that determines the presentation time of the frame. This relationship will only hold true, when each data chunk that is provided via GetNextChunk() contains data for exactly one encoded video frame (one complete frame) - not more and not less. We can decode data chunks that contain partial video frame data, too. In that case, you cannot trust the value of the start_time field in fHeader. We simply have no logic in place to establish a meaningful relationship between an incomplete frame and the start time it should be presented. Though this might change in the future. We can decode data chunks that contain more than one video frame, too. In that case, you cannot trust the value of the start_time field in fHeader. We simply have no logic in place to track the start_time across multiple video frames. So a meaningful relationship between the 2nd, 3rd, ... frame and the start time it should be presented isn't established at the moment. Though this might change in the future. More over the fOutputFrameRate variable is updated for every decoded video frame. On first call, the member variables fSwsContext / fFormatConversionFunc are initialized. \return B_OK, when we successfully decoded one video frame */ status_t AVCodecDecoder::_DecodeNextVideoFrame() { assert(fTempPacket.size >= 0); while (true) { media_header chunkMediaHeader; if (fTempPacket.size == 0) { // Our packet buffer is empty, so fill it now. status_t getNextChunkStatus = GetNextChunk(&fChunkBuffer, &fChunkBufferSize, &chunkMediaHeader); switch (getNextChunkStatus) { case B_OK: break; case B_LAST_BUFFER_ERROR: return _FlushOneVideoFrameFromDecoderBuffer(); default: TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from " "GetNextChunk(): %s\n", strerror(err)); return getNextChunkStatus; } fTempPacket.data = static_cast<uint8_t*>(const_cast<void*>( fChunkBuffer)); fTempPacket.size = fChunkBufferSize; fContext->reordered_opaque = chunkMediaHeader.start_time; // Let ffmpeg handle the relationship between start_time and // decoded video frame. // // Explanation: // The received chunk buffer may not contain the next video // frame to be decoded, due to frame reordering (e.g. MPEG1/2 // provides encoded video frames in a different order than the // decoded video frame). // // FIXME: Research how to establish a meaningful relationship // between start_time and decoded video frame when the received // chunk buffer contains partial video frames. Maybe some data // formats contain time stamps (ake pts / dts fields) that can // be evaluated by FFMPEG. But as long as I don't have such // video data to test it, it makes no sense to implement it. // // FIXME: Implement tracking start_time of video frames // originating in data chunks that encode more than one video // frame at a time. In that case on would increment the // start_time for each consecutive frame of such a data chunk // (like it is done for audio frame decoding). But as long as // I don't have such video data to test it, it makes no sense // to implement it. #ifdef LOG_STREAM_TO_FILE if (sDumpedPackets < 100) { sStreamLogFile.Write(fChunkBuffer, fChunkBufferSize); printf("wrote %ld bytes\n", fChunkBufferSize); sDumpedPackets++; } else if (sDumpedPackets == 100) sStreamLogFile.Unset(); #endif } #if DO_PROFILING bigtime_t startTime = system_time(); #endif // NOTE: In the FFMPEG 0.10.2 code example decoding_encoding.c, the // length returned by avcodec_decode_video2() is used to update the // packet buffer size (here it is fTempPacket.size). This way the // packet buffer is allowed to contain incomplete frames so we are // required to buffer the packets between different calls to // _DecodeNextVideoFrame(). int gotPicture = 0; int decodedDataSizeInBytes = avcodec_decode_video2(fContext, fRawDecodedPicture, &gotPicture, &fTempPacket); if (decodedDataSizeInBytes < 0) { TRACE("[v] AVCodecDecoder: ignoring error in decoding frame %lld:" " %d\n", fFrame, len); // NOTE: An error from avcodec_decode_video2() is ignored by the // FFMPEG 0.10.2 example decoding_encoding.c. Only the packet // buffers are flushed accordingly fTempPacket.data = NULL; fTempPacket.size = 0; continue; } fTempPacket.size -= decodedDataSizeInBytes; fTempPacket.data += decodedDataSizeInBytes; bool gotNoPictureYet = gotPicture == 0; if (gotNoPictureYet) { TRACE("frame %lld - no picture yet, decodedDataSizeInBytes: %d, " "chunk size: %ld\n", fFrame, decodedDataSizeInBytes, fChunkBufferSize); continue; } #if DO_PROFILING bigtime_t formatConversionStart = system_time(); #endif _HandleNewVideoFrameAndUpdateSystemState(); #if DO_PROFILING bigtime_t doneTime = system_time(); decodingTime += formatConversionStart - startTime; conversionTime += doneTime - formatConversionStart; profileCounter++; if (!(fFrame % 5)) { if (info) { printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required " "%Ld\n", decodingTime / profileCounter, conversionTime / profileCounter, fFrame, info->time_to_decode); } else { printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required " "%Ld\n", decodingTime / profileCounter, conversionTime / profileCounter, fFrame, bigtime_t(1000000LL / fOutputFrameRate)); } decodingTime = 0; conversionTime = 0; profileCounter = 0; } #endif 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; } }
void ToneProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* io_name) { FPRINTF(stderr, "ToneProducer::Connect\n"); // If something earlier failed, Connect() might still be called, but with a non-zero // error code. When that happens we simply unreserve the connection and do // nothing else. if (error) { mOutput.destination = media_destination::null; mOutput.format = mPreferredFormat; return; } // old workaround for format bug: Connect() receives the format data from the // input returned from BBufferConsumer::Connected(). // // char formatStr[256]; // string_for_format(format, formatStr, 255); // FPRINTF(stderr, "\trequested format: %s\n", formatStr); // if(format.type != B_MEDIA_RAW_AUDIO) { // // +++++ this is NOT proper behavior // // but it works // FPRINTF(stderr, "\tcorrupted format; falling back to last suggested format\n"); // format = mOutput.format; // } // // Okay, the connection has been confirmed. Record the destination and format // that we agreed on, and report our connection name again. mOutput.destination = destination; mOutput.format = format; strncpy(io_name, mOutput.name, B_MEDIA_NAME_LENGTH); // Now that we're connected, we can determine our downstream latency. // Do so, then make sure we get our events early enough. media_node_id id; FindLatencyFor(mOutput.destination, &mLatency, &id); FPRINTF(stderr, "\tdownstream latency = %" B_PRIdBIGTIME "\n", mLatency); // Use a dry run to see how long it takes me to fill a buffer of data bigtime_t start, produceLatency; size_t samplesPerBuffer = mOutput.format.u.raw_audio.buffer_size / sizeof(float); size_t framesPerBuffer = samplesPerBuffer / mOutput.format.u.raw_audio.channel_count; float* data = new float[samplesPerBuffer]; mTheta = 0; start = ::system_time(); FillSineBuffer(data, framesPerBuffer, mOutput.format.u.raw_audio.channel_count==2); produceLatency = ::system_time(); mInternalLatency = produceLatency - start; // +++++ e.moon [13jun99]: fiddling with latency, ick mInternalLatency += 20000LL; delete [] data; FPRINTF(stderr, "\tbuffer-filling took %" B_PRIdBIGTIME " usec on this machine\n", mInternalLatency); SetEventLatency(mLatency + mInternalLatency); // reset our buffer duration, etc. to avoid later calculations // +++++ e.moon 11jun99: crashes w/ divide-by-zero when connecting to LoggingConsumer ASSERT(mOutput.format.u.raw_audio.frame_rate); bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(mOutput.format.u.raw_audio.frame_rate); SetBufferDuration(duration); // Set up the buffer group for our connection, as long as nobody handed us a // buffer group (via SetBufferGroup()) prior to this. That can happen, for example, // if the consumer calls SetOutputBuffersFor() on us from within its Connected() // method. if (!mBufferGroup) AllocateBuffers(); }
bigtime_t Clock::Count2USec(bigtime_t t) { return bigtime_t(1000000.0*(t/((double)performanceFrequency))); }
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; } }
void VideoProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* _name) { FUNCTION("Connect() %ldx%ld\n", format.u.raw_video.display.line_width, format.u.raw_video.display.line_count); if (fConnected) { ERROR("Connect() - already connected.\n"); return; } if (source != fOutput.source) { ERROR("Connect() - wrong source.\n"); return; } if (error != B_OK) { ERROR("Connect() - consumer error: %s\n", strerror(error)); return; } if (!const_cast<media_format*>(&format)->Matches(&fOutput.format)) { ERROR("Connect() - format mismatch.\n"); return; } fOutput.destination = destination; strcpy(_name, fOutput.name); fConnectedFormat = format.u.raw_video; fBufferDuration = 20000; if (fConnectedFormat.field_rate != 0.0f) { fPerformanceTimeBase = fPerformanceTimeBase + (bigtime_t)((fFrame - fFrameBase) * 1000000LL / fConnectedFormat.field_rate); fFrameBase = fFrame; fBufferDuration = bigtime_t(1000000LL / fConnectedFormat.field_rate); } if (fConnectedFormat.display.bytes_per_row == 0) { ERROR("Connect() - connected format still has BPR wildcard!\n"); fConnectedFormat.display.bytes_per_row = 4 * fConnectedFormat.display.line_width; } // Create the buffer group if (fUsedBufferGroup == NULL) { fBufferGroup = new BBufferGroup(fConnectedFormat.display.bytes_per_row * fConnectedFormat.display.line_count, BUFFER_COUNT); status_t err = fBufferGroup->InitCheck(); if (err < B_OK) { delete fBufferGroup; fBufferGroup = NULL; ERROR("Connect() - buffer group error: %s\n", strerror(err)); return; } fUsedBufferGroup = fBufferGroup; } // get the latency fBufferLatency = (BUFFER_COUNT - 1) * fBufferDuration; int32 bufferCount; if (fUsedBufferGroup->CountBuffers(&bufferCount) == B_OK) { // recompute the latency fBufferLatency = (bufferCount - 1) * fBufferDuration; } bigtime_t latency = 0; media_node_id tsID = 0; FindLatencyFor(fOutput.destination, &latency, &tsID); SetEventLatency(latency + fBufferLatency); fConnected = true; fEnabled = true; // Tell frame generation thread to recalculate delay value release_sem(fFrameSync); }
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? }
void AudioProducer::Connect(status_t error, const media_source& source, const media_destination& destination, const media_format& format, char* _name) { TRACE("AudioProducer::Connect(%s)\n", strerror(error)); // If something earlier failed, Connect() might still be called, but with // a non-zero error code. When that happens we simply unreserve the // connection and do nothing else. if (error != B_OK) { fOutput.destination = media_destination::null; fOutput.format = fPreferredFormat; return; } // Okay, the connection has been confirmed. Record the destination and // format that we agreed on, and report our connection name again. fOutput.destination = destination; fOutput.format = format; strncpy(_name, fOutput.name, B_MEDIA_NAME_LENGTH); // tell our audio supplier about the format if (fSupplier) { TRACE("AudioProducer::Connect() fSupplier->SetFormat()\n"); fSupplier->SetFormat(fOutput.format); } TRACE("AudioProducer::Connect() FindLatencyFor()\n"); // Now that we're connected, we can determine our downstream latency. // Do so, then make sure we get our events early enough. media_node_id id; FindLatencyFor(fOutput.destination, &fLatency, &id); // Use a dry run to see how long it takes me to fill a buffer of data size_t sampleSize = fOutput.format.u.raw_audio.format & media_raw_audio_format::B_AUDIO_SIZE_MASK; size_t samplesPerBuffer = fOutput.format.u.raw_audio.buffer_size / sampleSize; fInternalLatency = estimate_internal_latency(fOutput.format); if (!fLowLatency) fInternalLatency *= 32; SetEventLatency(fLatency + fInternalLatency); // reset our buffer duration, etc. to avoid later calculations bigtime_t duration = bigtime_t(1000000) * samplesPerBuffer / bigtime_t(fOutput.format.u.raw_audio.frame_rate * fOutput.format.u.raw_audio.channel_count); TRACE("AudioProducer::Connect() SetBufferDuration(%lld)\n", duration); SetBufferDuration(duration); TRACE("AudioProducer::Connect() _AllocateBuffers()\n"); // Set up the buffer group for our connection, as long as nobody handed // us a buffer group (via SetBufferGroup()) prior to this. That can // happen, for example, if the consumer calls SetOutputBuffersFor() on // us from within its Connected() method. if (fBufferGroup == NULL) _AllocateBuffers(fOutput.format); TRACE("AudioProducer::Connect() done\n"); }
// _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; }
// 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; }