FFMPEG Plugin: Refactor out actual audio decoding part.
- Main reason for this refactoring is to increase readability and thus make the audio decode path more comprehensible. - Added documentation for the new method accordingly. - Small refactoring for detecting when to update fRawDecodedAudio's properties. This is a preparation step for factoring out the flushing of the fDecodedDataBuffer in a later commit. - No functional change intended.
This commit is contained in:
@@ -668,7 +668,6 @@ AVCodecDecoder::_DecodeNextAudioFrame()
|
|||||||
= static_cast<uint8_t*>(malloc(maximumSizeOfDecodedData));
|
= static_cast<uint8_t*>(malloc(maximumSizeOfDecodedData));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool firstAudioFramesCopiedToRawDecodedAudio = false;
|
|
||||||
avcodec_get_frame_defaults(fRawDecodedAudio);
|
avcodec_get_frame_defaults(fRawDecodedAudio);
|
||||||
fRawDecodedAudio->data[0] = fDecodedData;
|
fRawDecodedAudio->data[0] = fDecodedData;
|
||||||
|
|
||||||
@@ -699,6 +698,9 @@ AVCodecDecoder::_DecodeNextAudioFrame()
|
|||||||
|
|
||||||
memcpy(fRawDecodedAudio->data[0], fDecodedDataBuffer->data[0]
|
memcpy(fRawDecodedAudio->data[0], fDecodedDataBuffer->data[0]
|
||||||
+ fDecodedDataBufferOffset, remainingSize);
|
+ fDecodedDataBufferOffset, remainingSize);
|
||||||
|
|
||||||
|
bool firstAudioFramesCopiedToRawDecodedAudio
|
||||||
|
= fRawDecodedAudio->data[0] != fDecodedData;
|
||||||
if (!firstAudioFramesCopiedToRawDecodedAudio) {
|
if (!firstAudioFramesCopiedToRawDecodedAudio) {
|
||||||
fRawDecodedAudio->format = fDecodedDataBuffer->format;
|
fRawDecodedAudio->format = fDecodedDataBuffer->format;
|
||||||
fRawDecodedAudio->pkt_dts = fDecodedDataBuffer->pkt_dts;
|
fRawDecodedAudio->pkt_dts = fDecodedDataBuffer->pkt_dts;
|
||||||
@@ -709,8 +711,6 @@ AVCodecDecoder::_DecodeNextAudioFrame()
|
|||||||
// - fContext->sample_rate
|
// - fContext->sample_rate
|
||||||
// Those fields a are needed to allow the client of
|
// Those fields a are needed to allow the client of
|
||||||
// BMediaDecoder::Decode() to detect an audio format change.
|
// BMediaDecoder::Decode() to detect an audio format change.
|
||||||
|
|
||||||
firstAudioFramesCopiedToRawDecodedAudio = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fDecodedDataBufferOffset += remainingSize;
|
fDecodedDataBufferOffset += remainingSize;
|
||||||
@@ -759,11 +759,14 @@ AVCodecDecoder::_DecodeNextAudioFrame()
|
|||||||
// see _LoadNextVideoChunkIfNeededAndAssignStartTime()
|
// see _LoadNextVideoChunkIfNeededAndAssignStartTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
avcodec_get_frame_defaults(fDecodedDataBuffer);
|
status_t decodingStatus
|
||||||
int gotFrame = 0;
|
= _DecodeSomeAudioFramesIntoEmptyDecodedDataBuffer();
|
||||||
int usedBytes = avcodec_decode_audio4(fContext,
|
if (decodingStatus != B_OK) {
|
||||||
fDecodedDataBuffer, &gotFrame, &fTempPacket);
|
// Assume the audio decoded until now is broken so replace it with
|
||||||
if (usedBytes < 0 && !fAudioDecodeError) {
|
// some silence.
|
||||||
|
memset(fDecodedData, 0, fRawDecodedAudio->data[0] - fDecodedData);
|
||||||
|
|
||||||
|
if (!fAudioDecodeError) {
|
||||||
// Report failure if not done already
|
// Report failure if not done already
|
||||||
int32 chunkBufferOffset = fTempPacket.data
|
int32 chunkBufferOffset = fTempPacket.data
|
||||||
- static_cast<uint8_t*>(const_cast<void*>(fChunkBuffer));
|
- static_cast<uint8_t*>(const_cast<void*>(fChunkBuffer));
|
||||||
@@ -772,34 +775,8 @@ AVCodecDecoder::_DecodeNextAudioFrame()
|
|||||||
fTempPacket.size, chunkBufferOffset);
|
fTempPacket.size, chunkBufferOffset);
|
||||||
fAudioDecodeError = true;
|
fAudioDecodeError = true;
|
||||||
}
|
}
|
||||||
if (usedBytes <= 0) {
|
} else
|
||||||
// Error or failure to produce decompressed output.
|
|
||||||
// Skip the temp packet data entirely.
|
|
||||||
fTempPacket.size = 0;
|
|
||||||
|
|
||||||
// Assume the audio decoded until now is broken so replace it with
|
|
||||||
// some silence.
|
|
||||||
memset(fDecodedData, 0, fRawDecodedAudio->data[0] - fDecodedData);
|
|
||||||
fDecodedDataBufferOffset = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fAudioDecodeError = false;
|
fAudioDecodeError = false;
|
||||||
fDecodedDataBufferSize = 0;
|
|
||||||
fDecodedDataBufferOffset = 0;
|
|
||||||
|
|
||||||
fTempPacket.data += usedBytes;
|
|
||||||
fTempPacket.size -= usedBytes;
|
|
||||||
|
|
||||||
bool gotNoAudioFrame = gotFrame == 0;
|
|
||||||
if (gotNoAudioFrame)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
fDecodedDataBufferSize = av_samples_get_buffer_size(NULL,
|
|
||||||
fContext->channels, fDecodedDataBuffer->nb_samples,
|
|
||||||
fContext->sample_fmt, 1);
|
|
||||||
if (fDecodedDataBufferSize < 0)
|
|
||||||
fDecodedDataBufferSize = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fFrame += fRawDecodedAudio->nb_samples;
|
fFrame += fRawDecodedAudio->nb_samples;
|
||||||
@@ -817,6 +794,75 @@ AVCodecDecoder::_DecodeNextAudioFrame()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Tries to decode at least one audio frame and store it in the
|
||||||
|
fDecodedDataBuffer.
|
||||||
|
|
||||||
|
This function assumes to be called only when the following assumptions
|
||||||
|
hold true:
|
||||||
|
1. fDecodedDataBufferSize equals zero.
|
||||||
|
2. fTempPacket.size is greater than zero.
|
||||||
|
|
||||||
|
After this function returns successfully the caller can safely make the
|
||||||
|
following assumptions:
|
||||||
|
1. fDecodedDataBufferSize is greater than zero.
|
||||||
|
2. fTempPacket was updated to exclude the data chunk that was consumed
|
||||||
|
by avcodec_decode_audio4().
|
||||||
|
3. fDecodedDataBufferOffset is set to zero.
|
||||||
|
|
||||||
|
When this function failed to decode at least one audio frame due to a
|
||||||
|
decoding error the caller can safely make the following assumptions:
|
||||||
|
1. fDecodedDataBufferSize equals zero.
|
||||||
|
2. fTempPacket.size equals zero.
|
||||||
|
|
||||||
|
Note: It is possible that there wasn't any audio frame decoded into
|
||||||
|
fDecodedDataBuffer after calling this function. This is normal and can
|
||||||
|
happen when there was either a decoding error or there is some decoding
|
||||||
|
delay in FFMPEGs audio decoder. Another call to this method is totally
|
||||||
|
safe and is even expected as long as the calling assumptions hold true.
|
||||||
|
|
||||||
|
\returns B_OK Decoding successful. fDecodedDataBuffer contains decoded
|
||||||
|
audio frames only when fDecodedDataBufferSize is greater than zero.
|
||||||
|
fDecodedDataBuffer is empty, when avcodec_decode_audio4() didn't return
|
||||||
|
audio frames due to delayed decoding.
|
||||||
|
\returns B_ERROR Decoding failed thus fDecodedDataBuffer contains no audio
|
||||||
|
frames.
|
||||||
|
*/
|
||||||
|
status_t
|
||||||
|
AVCodecDecoder::_DecodeSomeAudioFramesIntoEmptyDecodedDataBuffer()
|
||||||
|
{
|
||||||
|
assert(fDecodedDataBufferSize == 0);
|
||||||
|
assert(fTempPacket.size > 0);
|
||||||
|
|
||||||
|
avcodec_get_frame_defaults(fDecodedDataBuffer);
|
||||||
|
fDecodedDataBufferOffset = 0;
|
||||||
|
int gotAudioFrame = 0;
|
||||||
|
|
||||||
|
int encodedDataSizeInBytes = avcodec_decode_audio4(fContext,
|
||||||
|
fDecodedDataBuffer, &gotAudioFrame, &fTempPacket);
|
||||||
|
if (encodedDataSizeInBytes <= 0) {
|
||||||
|
// Error or failure to produce decompressed output.
|
||||||
|
// Skip the temp packet data entirely.
|
||||||
|
fTempPacket.size = 0;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
fTempPacket.data += encodedDataSizeInBytes;
|
||||||
|
fTempPacket.size -= encodedDataSizeInBytes;
|
||||||
|
|
||||||
|
bool gotNoAudioFrame = gotAudioFrame == 0;
|
||||||
|
if (gotNoAudioFrame)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
fDecodedDataBufferSize = av_samples_get_buffer_size(NULL,
|
||||||
|
fContext->channels, fDecodedDataBuffer->nb_samples,
|
||||||
|
fContext->sample_fmt, 1);
|
||||||
|
if (fDecodedDataBufferSize < 0)
|
||||||
|
fDecodedDataBufferSize = 0;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Decodes next video frame.
|
/*! \brief Decodes next video frame.
|
||||||
|
|
||||||
We decode exactly one video frame into fDecodedData. To achieve this goal,
|
We decode exactly one video frame into fDecodedData. To achieve this goal,
|
||||||
@@ -877,7 +923,7 @@ AVCodecDecoder::_DecodeNextVideoFrame()
|
|||||||
|
|
||||||
if (loadingChunkStatus != B_OK) {
|
if (loadingChunkStatus != B_OK) {
|
||||||
TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from "
|
TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from "
|
||||||
"GetNextChunk(): %s\n", strerror(err));
|
"GetNextChunk(): %s\n", strerror(loadingChunkStatus));
|
||||||
return loadingChunkStatus;
|
return loadingChunkStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ private:
|
|||||||
media_header* mediaHeader,
|
media_header* mediaHeader,
|
||||||
media_decode_info* info);
|
media_decode_info* info);
|
||||||
status_t _DecodeNextAudioFrame();
|
status_t _DecodeNextAudioFrame();
|
||||||
|
status_t _DecodeSomeAudioFramesIntoEmptyDecodedDataBuffer();
|
||||||
status_t _DecodeNextVideoFrame();
|
status_t _DecodeNextVideoFrame();
|
||||||
void _ApplyEssentialVideoContainerPropertiesToContext();
|
void _ApplyEssentialVideoContainerPropertiesToContext();
|
||||||
status_t _LoadNextVideoChunkIfNeededAndAssignStartTime();
|
status_t _LoadNextVideoChunkIfNeededAndAssignStartTime();
|
||||||
|
|||||||
Reference in New Issue
Block a user