FFMPEG Plugin: Implement flushing of video frames from decoder.
- Video frames still contained in the video decoder are now flushed when there are no encoded data chunks left (signaled by B_LAST_BUFFER_ERROR). - New code was successfully tested with mpeg2_decoder_test. The last test image is now decoded, too, resulting in a bump of the expected number of decoded images. - Refactor code to support flushing -and- keep the readability at a sane level. - Remove some uncommented code for the sake of readability. - Documentation updated accordingly.
This commit is contained in:
@@ -735,10 +735,17 @@ AVCodecDecoder::_DecodeNextVideoFrame()
|
|||||||
// Our packet buffer is empty, so fill it now.
|
// Our packet buffer is empty, so fill it now.
|
||||||
status_t getNextChunkStatus = GetNextChunk(&fChunkBuffer,
|
status_t getNextChunkStatus = GetNextChunk(&fChunkBuffer,
|
||||||
&fChunkBufferSize, &chunkMediaHeader);
|
&fChunkBufferSize, &chunkMediaHeader);
|
||||||
if (getNextChunkStatus != B_OK) {
|
switch (getNextChunkStatus) {
|
||||||
TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from "
|
case B_OK:
|
||||||
"GetNextChunk(): %s\n", strerror(err));
|
break;
|
||||||
return getNextChunkStatus;
|
|
||||||
|
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*>(
|
fTempPacket.data = static_cast<uint8_t*>(const_cast<void*>(
|
||||||
@@ -807,75 +814,117 @@ AVCodecDecoder::_DecodeNextVideoFrame()
|
|||||||
fTempPacket.size -= decodedDataSizeInBytes;
|
fTempPacket.size -= decodedDataSizeInBytes;
|
||||||
fTempPacket.data += decodedDataSizeInBytes;
|
fTempPacket.data += decodedDataSizeInBytes;
|
||||||
|
|
||||||
//TRACE("FFDEC: PTS = %d:%d:%d.%d - fContext->frame_number = %ld "
|
bool gotNoPictureYet = gotPicture == 0;
|
||||||
// "fContext->frame_rate = %ld\n", (int)(fContext->pts / (60*60*1000000)),
|
if (gotNoPictureYet) {
|
||||||
// (int)(fContext->pts / (60*1000000)), (int)(fContext->pts / (1000000)),
|
TRACE("frame %lld - no picture yet, decodedDataSizeInBytes: %d, "
|
||||||
// (int)(fContext->pts % 1000000), fContext->frame_number,
|
"chunk size: %ld\n", fFrame, decodedDataSizeInBytes,
|
||||||
// fContext->frame_rate);
|
fChunkBufferSize);
|
||||||
//TRACE("FFDEC: PTS = %d:%d:%d.%d - fContext->frame_number = %ld "
|
continue;
|
||||||
// "fContext->frame_rate = %ld\n",
|
}
|
||||||
// (int)(fRawDecodedPicture->pts / (60*60*1000000)),
|
|
||||||
// (int)(fRawDecodedPicture->pts / (60*1000000)),
|
|
||||||
// (int)(fRawDecodedPicture->pts / (1000000)),
|
|
||||||
// (int)(fRawDecodedPicture->pts % 1000000), fContext->frame_number,
|
|
||||||
// fContext->frame_rate);
|
|
||||||
|
|
||||||
if (gotPicture) {
|
|
||||||
#if DO_PROFILING
|
#if DO_PROFILING
|
||||||
bigtime_t formatConversionStart = system_time();
|
bigtime_t formatConversionStart = system_time();
|
||||||
#endif
|
#endif
|
||||||
// TRACE("ONE FRAME OUT !! len=%d size=%ld (%s)\n", len, size,
|
|
||||||
// pixfmt_to_string(fContext->pix_fmt));
|
|
||||||
|
|
||||||
_UpdateMediaHeaderForVideoFrame();
|
_HandleNewVideoFrameAndUpdateSystemState();
|
||||||
_DeinterlaceAndColorConvertVideoFrame();
|
|
||||||
|
|
||||||
ConvertAVCodecContextToVideoFrameRate(*fContext, fOutputFrameRate);
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! \brief Executes all steps needed for a freshly decoded video frame.
|
||||||
|
|
||||||
|
\see _UpdateMediaHeaderForVideoFrame() and
|
||||||
|
\see _DeinterlaceAndColorConvertVideoFrame() for when you are allowed to
|
||||||
|
call this method.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
AVCodecDecoder::_HandleNewVideoFrameAndUpdateSystemState()
|
||||||
|
{
|
||||||
|
_UpdateMediaHeaderForVideoFrame();
|
||||||
|
_DeinterlaceAndColorConvertVideoFrame();
|
||||||
|
|
||||||
|
ConvertAVCodecContextToVideoFrameRate(*fContext, fOutputFrameRate);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
dump_ffframe(fRawDecodedPicture, "ffpict");
|
dump_ffframe(fRawDecodedPicture, "ffpict");
|
||||||
// dump_ffframe(fPostProcessedDecodedPicture, "opict");
|
// dump_ffframe(fPostProcessedDecodedPicture, "opict");
|
||||||
#endif
|
#endif
|
||||||
fFrame++;
|
fFrame++;
|
||||||
|
}
|
||||||
|
|
||||||
#if DO_PROFILING
|
|
||||||
bigtime_t doneTime = system_time();
|
/*! \brief Flushes one video frame - if any - still buffered by the decoder.
|
||||||
decodingTime += formatConversionStart - startTime;
|
|
||||||
conversionTime += doneTime - formatConversionStart;
|
Some FFMPEG decoder are buffering video frames. To retrieve those buffered
|
||||||
profileCounter++;
|
frames the decoder needs to be told so.
|
||||||
if (!(fFrame % 5)) {
|
|
||||||
if (info) {
|
The intended use of this method is to call it, once there are no more data
|
||||||
printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required "
|
chunks for decoding left. Reframed in other words: Once GetNextChunk()
|
||||||
"%Ld\n",
|
returns with status B_LAST_BUFFER_ERROR it is time to start flushing.
|
||||||
decodingTime / profileCounter,
|
|
||||||
conversionTime / profileCounter,
|
\returns B_OK Retrieved one video frame, handled it accordingly and updated
|
||||||
fFrame, info->time_to_decode);
|
the system state accordingly.
|
||||||
} else {
|
There maybe more video frames left. So it is valid for the client of
|
||||||
printf("[v] profile: d1 = %lld, d2 = %lld (%lld) required "
|
AVCodecDecoder to call it one more time.
|
||||||
"%Ld\n",
|
|
||||||
decodingTime / profileCounter,
|
\returns B_LAST_BUFFER_ERROR No video frame left.
|
||||||
conversionTime / profileCounter,
|
The client of the AVCodecDecoder should stop calling it now.
|
||||||
fFrame, bigtime_t(1000000LL / fOutputFrameRate));
|
*/
|
||||||
}
|
status_t
|
||||||
decodingTime = 0;
|
AVCodecDecoder::_FlushOneVideoFrameFromDecoderBuffer()
|
||||||
conversionTime = 0;
|
{
|
||||||
profileCounter = 0;
|
// Create empty fTempPacket to tell the video decoder it is time to flush
|
||||||
}
|
fTempPacket.data = NULL;
|
||||||
#endif
|
fTempPacket.size = 0;
|
||||||
return B_OK;
|
|
||||||
} else {
|
int gotPicture = 0;
|
||||||
TRACE("frame %lld - no picture yet, len: %d, chunk size: %ld\n",
|
avcodec_decode_video2(fContext, fRawDecodedPicture, &gotPicture,
|
||||||
fFrame, len, size);
|
&fTempPacket);
|
||||||
}
|
// We are only interested in complete frames now, so ignore the return
|
||||||
|
// value.
|
||||||
|
|
||||||
|
if (gotPicture == 0) {
|
||||||
|
// video buffer is flushed successfully
|
||||||
|
return B_LAST_BUFFER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_HandleNewVideoFrameAndUpdateSystemState();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Updates relevant fields of the class member fHeader with the
|
/*! \brief Updates relevant fields of the class member fHeader with the
|
||||||
properties of the most recently decoded video frame.
|
properties of the most recently decoded video frame.
|
||||||
|
|
||||||
It is assumed that this function is called in _DecodeNextVideoFrame() only
|
It is assumed that this function is called only when the following asserts
|
||||||
when the following asserts hold true:
|
hold true:
|
||||||
1. We actually got a new picture decoded by the video decoder.
|
1. We actually got a new picture decoded by the video decoder.
|
||||||
2. fHeader wasn't updated for the new picture yet. You MUST call this
|
2. fHeader wasn't updated for the new picture yet. You MUST call this
|
||||||
method only once per decoded video frame.
|
method only once per decoded video frame.
|
||||||
@@ -920,15 +969,15 @@ AVCodecDecoder::_UpdateMediaHeaderForVideoFrame()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief This function applies deinterlacing (only if needed) and color conversion
|
/*! \brief This function applies deinterlacing (only if needed) and color
|
||||||
to the video frame in fRawDecodedPicture.
|
conversion to the video frame in fRawDecodedPicture.
|
||||||
|
|
||||||
It is assumed that fRawDecodedPicture wasn't deinterlaced and color
|
It is assumed that fRawDecodedPicture wasn't deinterlaced and color
|
||||||
converted yet (otherwise this function behaves in unknown manners).
|
converted yet (otherwise this function behaves in unknown manners).
|
||||||
|
|
||||||
You should only call this function in _DecodeNextVideoFrame() when we
|
You should only call this function when you got a new picture decoded by
|
||||||
got a new picture decoded by the video decoder and the fHeader variable was
|
the video decoder and the fHeader variable was updated accordingly (\see
|
||||||
updated accordingly (@see _UpdateMediaHeaderForVideoFrame()).
|
_UpdateMediaHeaderForVideoFrame()).
|
||||||
|
|
||||||
When this function finishes the postprocessed video frame will be available
|
When this function finishes the postprocessed video frame will be available
|
||||||
in fPostProcessedDecodedPicture and fDecodedData (fDecodedDataSizeInBytes
|
in fPostProcessedDecodedPicture and fDecodedData (fDecodedDataSizeInBytes
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ private:
|
|||||||
media_header* mediaHeader,
|
media_header* mediaHeader,
|
||||||
media_decode_info* info);
|
media_decode_info* info);
|
||||||
status_t _DecodeNextVideoFrame();
|
status_t _DecodeNextVideoFrame();
|
||||||
|
void _HandleNewVideoFrameAndUpdateSystemState();
|
||||||
|
status_t _FlushOneVideoFrameFromDecoderBuffer();
|
||||||
void _UpdateMediaHeaderForVideoFrame();
|
void _UpdateMediaHeaderForVideoFrame();
|
||||||
void _DeinterlaceAndColorConvertVideoFrame();
|
void _DeinterlaceAndColorConvertVideoFrame();
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
Successful completion of this test results in a series of PNG image
|
Successful completion of this test results in a series of PNG image
|
||||||
files created at the same location you start the test from.
|
files created at the same location you start the test from.
|
||||||
|
|
||||||
The originally included test file results in 84 PNG images,
|
The originally included test file results in 85 PNG images,
|
||||||
representing a movie sequence with the actress Anne Hathaway.
|
representing a movie sequence with the actress Anne Hathaway.
|
||||||
This test file has the following properties:
|
This test file has the following properties:
|
||||||
- The first frames cannot be decoded, due to missing I-Frames
|
- The first frames cannot be decoded, due to missing I-Frames
|
||||||
|
|||||||
Reference in New Issue
Block a user