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:
Colin Günther 2014-08-04 18:15:02 +02:00
parent 09785ce274
commit a335ec823a
3 changed files with 114 additions and 63 deletions

View File

@ -735,10 +735,17 @@ AVCodecDecoder::_DecodeNextVideoFrame()
// Our packet buffer is empty, so fill it now.
status_t getNextChunkStatus = GetNextChunk(&fChunkBuffer,
&fChunkBufferSize, &chunkMediaHeader);
if (getNextChunkStatus != B_OK) {
TRACE("AVCodecDecoder::_DecodeNextVideoFrame(): error from "
"GetNextChunk(): %s\n", strerror(err));
return getNextChunkStatus;
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*>(
@ -807,75 +814,117 @@ AVCodecDecoder::_DecodeNextVideoFrame()
fTempPacket.size -= decodedDataSizeInBytes;
fTempPacket.data += decodedDataSizeInBytes;
//TRACE("FFDEC: PTS = %d:%d:%d.%d - fContext->frame_number = %ld "
// "fContext->frame_rate = %ld\n", (int)(fContext->pts / (60*60*1000000)),
// (int)(fContext->pts / (60*1000000)), (int)(fContext->pts / (1000000)),
// (int)(fContext->pts % 1000000), fContext->frame_number,
// fContext->frame_rate);
//TRACE("FFDEC: PTS = %d:%d:%d.%d - fContext->frame_number = %ld "
// "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);
bool gotNoPictureYet = gotPicture == 0;
if (gotNoPictureYet) {
TRACE("frame %lld - no picture yet, decodedDataSizeInBytes: %d, "
"chunk size: %ld\n", fFrame, decodedDataSizeInBytes,
fChunkBufferSize);
continue;
}
if (gotPicture) {
#if DO_PROFILING
bigtime_t formatConversionStart = system_time();
bigtime_t formatConversionStart = system_time();
#endif
// TRACE("ONE FRAME OUT !! len=%d size=%ld (%s)\n", len, size,
// pixfmt_to_string(fContext->pix_fmt));
_UpdateMediaHeaderForVideoFrame();
_DeinterlaceAndColorConvertVideoFrame();
_HandleNewVideoFrameAndUpdateSystemState();
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
dump_ffframe(fRawDecodedPicture, "ffpict");
// dump_ffframe(fPostProcessedDecodedPicture, "opict");
dump_ffframe(fRawDecodedPicture, "ffpict");
// dump_ffframe(fPostProcessedDecodedPicture, "opict");
#endif
fFrame++;
fFrame++;
}
#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;
} else {
TRACE("frame %lld - no picture yet, len: %d, chunk size: %ld\n",
fFrame, len, size);
}
/*! \brief Flushes one video frame - if any - still buffered by the decoder.
Some FFMPEG decoder are buffering video frames. To retrieve those buffered
frames the decoder needs to be told so.
The intended use of this method is to call it, once there are no more data
chunks for decoding left. Reframed in other words: Once GetNextChunk()
returns with status B_LAST_BUFFER_ERROR it is time to start flushing.
\returns B_OK Retrieved one video frame, handled it accordingly and updated
the system state accordingly.
There maybe more video frames left. So it is valid for the client of
AVCodecDecoder to call it one more time.
\returns B_LAST_BUFFER_ERROR No video frame left.
The client of the AVCodecDecoder should stop calling it now.
*/
status_t
AVCodecDecoder::_FlushOneVideoFrameFromDecoderBuffer()
{
// Create empty fTempPacket to tell the video decoder it is time to flush
fTempPacket.data = NULL;
fTempPacket.size = 0;
int gotPicture = 0;
avcodec_decode_video2(fContext, fRawDecodedPicture, &gotPicture,
&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
properties of the most recently decoded video frame.
It is assumed that this function is called in _DecodeNextVideoFrame() only
when the following asserts hold true:
It is assumed that this function is called only when the following asserts
hold true:
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
method only once per decoded video frame.
@ -920,15 +969,15 @@ AVCodecDecoder::_UpdateMediaHeaderForVideoFrame()
}
/*! \brief This function applies deinterlacing (only if needed) and color conversion
to the video frame in fRawDecodedPicture.
/*! \brief This function applies deinterlacing (only if needed) and color
conversion to the video frame in fRawDecodedPicture.
It is assumed that fRawDecodedPicture wasn't deinterlaced and color
converted yet (otherwise this function behaves in unknown manners).
You should only call this function in _DecodeNextVideoFrame() when we
got a new picture decoded by the video decoder and the fHeader variable was
updated accordingly (@see _UpdateMediaHeaderForVideoFrame()).
You should only call this function when you got a new picture decoded by
the video decoder and the fHeader variable was updated accordingly (\see
_UpdateMediaHeaderForVideoFrame()).
When this function finishes the postprocessed video frame will be available
in fPostProcessedDecodedPicture and fDecodedData (fDecodedDataSizeInBytes

View File

@ -66,6 +66,8 @@ private:
media_header* mediaHeader,
media_decode_info* info);
status_t _DecodeNextVideoFrame();
void _HandleNewVideoFrameAndUpdateSystemState();
status_t _FlushOneVideoFrameFromDecoderBuffer();
void _UpdateMediaHeaderForVideoFrame();
void _DeinterlaceAndColorConvertVideoFrame();

View File

@ -19,7 +19,7 @@
Successful completion of this test results in a series of PNG image
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.
This test file has the following properties:
- The first frames cannot be decoded, due to missing I-Frames