FFMPEG Plugin: Refactor start time handling in audio path.

- FFMPEG handles the relationship of start time between encoded and decoded
  audio data now by using the fTempPacket->dts and the
  fDecodedDataBuffer->pkt_dts fields. We still have to manually keep track of
  start times for consecutive audio frames though to support returning a number
  of audio frames that may assembled of partial AVFrames.

- The start time of the very first audio frame data packet returned by Decode()
  is now correctly calculated based on GetNextChunk() start times instead of
  being always zero.

- Introduce fRawDecodedAudio that serves as a container to store properties of
  the audio frames stored in fDecodedData. This prepares the population of the
  fHeader structure with audio frame properties needed to allow clients of
  BMediaDecoder::Decode() detect audio format changes in a later commit.

- Remove fStartTime as it is superflous now.
This commit is contained in:
Colin Günther
2014-08-21 21:46:22 +02:00
parent 8516a39e5d
commit 815d18fb53
2 changed files with 60 additions and 29 deletions
@@ -86,6 +86,7 @@ AVCodecDecoder::AVCodecDecoder()
fDecodedDataSizeInBytes(0), fDecodedDataSizeInBytes(0),
fPostProcessedDecodedPicture(avcodec_alloc_frame()), fPostProcessedDecodedPicture(avcodec_alloc_frame()),
fRawDecodedPicture(avcodec_alloc_frame()), fRawDecodedPicture(avcodec_alloc_frame()),
fRawDecodedAudio(avcodec_alloc_frame()),
fCodecInitDone(false), fCodecInitDone(false),
@@ -99,7 +100,6 @@ AVCodecDecoder::AVCodecDecoder()
fExtraDataSize(0), fExtraDataSize(0),
fBlockAlign(0), fBlockAlign(0),
fStartTime(0),
fOutputColorSpace(B_NO_COLOR_SPACE), fOutputColorSpace(B_NO_COLOR_SPACE),
fOutputFrameCount(0), fOutputFrameCount(0),
fOutputFrameRate(1.0), fOutputFrameRate(1.0),
@@ -147,6 +147,7 @@ AVCodecDecoder::~AVCodecDecoder()
av_free(fPostProcessedDecodedPicture); av_free(fPostProcessedDecodedPicture);
av_free(fRawDecodedPicture); av_free(fRawDecodedPicture);
av_free(fRawDecodedAudio);
av_free(fContext); av_free(fContext);
av_free(fDecodedDataBuffer); av_free(fDecodedDataBuffer);
@@ -274,7 +275,6 @@ AVCodecDecoder::SeekedTo(int64 frame, bigtime_t time)
fDecodedDataSizeInBytes = 0; fDecodedDataSizeInBytes = 0;
fFrame = frame; fFrame = frame;
fStartTime = time;
return ret; return ret;
} }
@@ -306,11 +306,6 @@ AVCodecDecoder::Decode(void* outBuffer, int64* outFrameCount,
if (!fCodecInitDone) if (!fCodecInitDone)
return B_NO_INIT; return B_NO_INIT;
// TRACE("[%c] AVCodecDecoder::Decode() for time %Ld\n", fIsAudio?('a'):('v'),
// fStartTime);
mediaHeader->start_time = fStartTime;
status_t ret; status_t ret;
if (fIsAudio) if (fIsAudio)
ret = _DecodeAudio(outBuffer, outFrameCount, mediaHeader, info); ret = _DecodeAudio(outBuffer, outFrameCount, mediaHeader, info);
@@ -402,7 +397,6 @@ AVCodecDecoder::_NegotiateAudioOutputFormat(media_format* inOutFormat)
int result = avcodec_open2(fContext, fCodec, NULL); int result = avcodec_open2(fContext, fCodec, NULL);
fCodecInitDone = (result >= 0); fCodecInitDone = (result >= 0);
fStartTime = 0;
fOutputFrameSize = sampleSize * outputAudioFormat.channel_count; fOutputFrameSize = sampleSize * outputAudioFormat.channel_count;
fOutputFrameCount = outputAudioFormat.buffer_size / fOutputFrameSize; fOutputFrameCount = outputAudioFormat.buffer_size / fOutputFrameSize;
fOutputFrameRate = outputAudioFormat.frame_rate; fOutputFrameRate = outputAudioFormat.frame_rate;
@@ -573,6 +567,7 @@ AVCodecDecoder::_DecodeAudio(void* outBuffer, int64* outFrameCount,
return audioDecodingStatus; return audioDecodingStatus;
*outFrameCount = fDecodedDataSizeInBytes / fOutputFrameSize; *outFrameCount = fDecodedDataSizeInBytes / fOutputFrameSize;
*mediaHeader = fHeader;
memcpy(outBuffer, fDecodedData, fDecodedDataSizeInBytes); memcpy(outBuffer, fDecodedData, fDecodedDataSizeInBytes);
fDecodedDataSizeInBytes = 0; fDecodedDataSizeInBytes = 0;
@@ -667,14 +662,17 @@ AVCodecDecoder::_DecodeNextAudioFrame()
// _DecodeNextAudioFrame needs to be called on empty fDecodedData only! // _DecodeNextAudioFrame needs to be called on empty fDecodedData only!
// If this assert holds wrong we have a bug somewhere. // If this assert holds wrong we have a bug somewhere.
if (fDecodedData == NULL) {
size_t maximumSizeOfDecodedData = fOutputFrameCount * fOutputFrameSize; size_t maximumSizeOfDecodedData = fOutputFrameCount * fOutputFrameSize;
if (fDecodedData == NULL)
fDecodedData fDecodedData
= static_cast<uint8_t*>(malloc(maximumSizeOfDecodedData)); = static_cast<uint8_t*>(malloc(maximumSizeOfDecodedData));
uint8_t* decodedData = fDecodedData; }
int64 currentFrameCount = 0; bool firstAudioFramesCopiedToRawDecodedAudio = false;
while (currentFrameCount < fOutputFrameCount) { avcodec_get_frame_defaults(fRawDecodedAudio);
fRawDecodedAudio->data[0] = fDecodedData;
while (fRawDecodedAudio->nb_samples < fOutputFrameCount) {
// Check conditions which would hint at broken code below. // Check conditions which would hint at broken code below.
if (fDecodedDataBufferSize < 0) { if (fDecodedDataBufferSize < 0) {
fprintf(stderr, "Decoding read past the end of the decoded data " fprintf(stderr, "Decoding read past the end of the decoded data "
@@ -692,20 +690,44 @@ AVCodecDecoder::_DecodeNextAudioFrame()
// invokation, which start at fDecodedDataBufferOffset // invokation, which start at fDecodedDataBufferOffset
// and are of fDecodedDataBufferSize. Copy those into the buffer, // and are of fDecodedDataBufferSize. Copy those into the buffer,
// but not more than it can hold. // but not more than it can hold.
int32 frames = min_c(fOutputFrameCount - currentFrameCount, int32 frames
= min_c(fOutputFrameCount - fRawDecodedAudio->nb_samples,
fDecodedDataBufferSize / fOutputFrameSize); fDecodedDataBufferSize / fOutputFrameSize);
if (frames == 0) if (frames == 0)
debugger("fDecodedDataBufferSize not multiple of frame size!"); debugger("fDecodedDataBufferSize not multiple of frame size!");
size_t remainingSize = frames * fOutputFrameSize; size_t remainingSize = frames * fOutputFrameSize;
memcpy(decodedData, fDecodedDataBuffer->data[0]
memcpy(fRawDecodedAudio->data[0], fDecodedDataBuffer->data[0]
+ fDecodedDataBufferOffset, remainingSize); + fDecodedDataBufferOffset, remainingSize);
if (!firstAudioFramesCopiedToRawDecodedAudio) {
fRawDecodedAudio->format = fDecodedDataBuffer->format;
fRawDecodedAudio->pkt_dts = fDecodedDataBuffer->pkt_dts;
// TODO: also store the following fContext fields in the
// opaque field of fRawDecodedAudio:
// - fContext->channels
// - fContext->sample_rate
// Those fields a are needed to allow the client of
// BMediaDecoder::Decode() to detect an audio format change.
firstAudioFramesCopiedToRawDecodedAudio = true;
}
fDecodedDataBufferOffset += remainingSize; fDecodedDataBufferOffset += remainingSize;
fDecodedDataBufferSize -= remainingSize; fDecodedDataBufferSize -= remainingSize;
decodedData += remainingSize; fRawDecodedAudio->data[0] += remainingSize;
currentFrameCount += frames; fRawDecodedAudio->nb_samples += frames;
fStartTime += (bigtime_t)((1000000LL * frames) / fOutputFrameRate);
// Update start times
bigtime_t framesTimeInterval = static_cast<bigtime_t>(
(1000000LL * frames) / fOutputFrameRate);
fDecodedDataBuffer->pkt_dts += framesTimeInterval;
fTempPacket.dts += framesTimeInterval;
// Note: Start time of fTempPacket is updated in case the
// fTempPacket contains more audio frames to decode.
continue; continue;
} }
if (fTempPacket.size == 0) { if (fTempPacket.size == 0) {
// Time to read the next chunk buffer. We use a separate // Time to read the next chunk buffer. We use a separate
// media_header, since the chunk header may not belong to // media_header, since the chunk header may not belong to
@@ -730,7 +752,11 @@ AVCodecDecoder::_DecodeNextAudioFrame()
fTempPacket.data fTempPacket.data
= static_cast<uint8_t*>(const_cast<void*>(fChunkBuffer)); = static_cast<uint8_t*>(const_cast<void*>(fChunkBuffer));
fTempPacket.size = fChunkBufferSize; fTempPacket.size = fChunkBufferSize;
fStartTime = chunkMediaHeader.start_time; fTempPacket.dts = chunkMediaHeader.start_time;
// Let FFMPEG handle the correct relationship between
// start_time and decoded audio frames. By doing so we are
// merely following the way it is done for the video path
// see _LoadNextVideoChunkIfNeededAndAssignStartTime()
} }
avcodec_get_frame_defaults(fDecodedDataBuffer); avcodec_get_frame_defaults(fDecodedDataBuffer);
@@ -750,8 +776,10 @@ AVCodecDecoder::_DecodeNextAudioFrame()
// Error or failure to produce decompressed output. // Error or failure to produce decompressed output.
// Skip the temp packet data entirely. // Skip the temp packet data entirely.
fTempPacket.size = 0; fTempPacket.size = 0;
// Assume the audio decoded until now is broken.
memset(fDecodedData, 0, decodedData - fDecodedData); // Assume the audio decoded until now is broken so replace it with
// some silence.
memset(fDecodedData, 0, fRawDecodedAudio->data[0] - fDecodedData);
fDecodedDataBufferOffset = 0; fDecodedDataBufferOffset = 0;
continue; continue;
} }
@@ -772,15 +800,18 @@ AVCodecDecoder::_DecodeNextAudioFrame()
fContext->sample_fmt, 1); fContext->sample_fmt, 1);
if (fDecodedDataBufferSize < 0) if (fDecodedDataBufferSize < 0)
fDecodedDataBufferSize = 0; fDecodedDataBufferSize = 0;
#ifdef DEBUG
dump_ffframe_audio(fDecodedDataBuffer, "ffaudio");
#endif
} }
fFrame += currentFrameCount; fFrame += fRawDecodedAudio->nb_samples;
fDecodedDataSizeInBytes = currentFrameCount * fOutputFrameSize; fHeader.start_time = fRawDecodedAudio->pkt_dts;
TRACE_AUDIO(" frame count: %lld current: %lld\n", currentFrameCount, fFrame); fDecodedDataSizeInBytes = fRawDecodedAudio->nb_samples * fOutputFrameSize;
#ifdef DEBUG
dump_ffframe_audio(fRawDecodedAudio, "ffaudi");
#endif
TRACE_AUDIO(" frame count: %lld current: %lld\n",
fRawDecodedAudio->nb_samples, fFrame);
return B_OK; return B_OK;
} }
@@ -94,6 +94,7 @@ private:
size_t fDecodedDataSizeInBytes; size_t fDecodedDataSizeInBytes;
AVFrame* fPostProcessedDecodedPicture; AVFrame* fPostProcessedDecodedPicture;
AVFrame* fRawDecodedPicture; AVFrame* fRawDecodedPicture;
AVFrame* fRawDecodedAudio;
bool fCodecInitDone; bool fCodecInitDone;
@@ -104,7 +105,6 @@ private:
int fExtraDataSize; int fExtraDataSize;
int fBlockAlign; int fBlockAlign;
bigtime_t fStartTime;
color_space fOutputColorSpace; color_space fOutputColorSpace;
int32 fOutputFrameCount; int32 fOutputFrameCount;
float fOutputFrameRate; float fOutputFrameRate;