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:
@@ -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.
|
||||||
|
|
||||||
size_t maximumSizeOfDecodedData = fOutputFrameCount * fOutputFrameSize;
|
if (fDecodedData == NULL) {
|
||||||
if (fDecodedData == NULL)
|
size_t maximumSizeOfDecodedData = fOutputFrameCount * fOutputFrameSize;
|
||||||
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user