FFMPEG Plugin: Automatic detection of video decoding parameters.
- Kudos to Marcus Overhagen for laying out the general idea of automatic detection by sharing some of his dvb code examples with me. - Simplify pixel format search code by removing the loop and let FFMPEG decide what pixel format to use based on the actual video data. - Automatically detect the video frame rate based on the actual video data. - Remove fOutputVideoFormat to avoid synchronizing values in two distinct places. The member variable fHeader is the main place for important decoder parameters now. - Introduce fOutputColorSpace containing the color space that was previously tracked in the fOutputVideoFormat member variable. - Update the documentation accordingly.
This commit is contained in:
@@ -78,7 +78,6 @@ AVCodecDecoder::AVCodecDecoder()
|
|||||||
:
|
:
|
||||||
fHeader(),
|
fHeader(),
|
||||||
fInputFormat(),
|
fInputFormat(),
|
||||||
fOutputVideoFormat(),
|
|
||||||
fFrame(0),
|
fFrame(0),
|
||||||
fIsAudio(false),
|
fIsAudio(false),
|
||||||
fCodec(NULL),
|
fCodec(NULL),
|
||||||
@@ -101,6 +100,7 @@ AVCodecDecoder::AVCodecDecoder()
|
|||||||
fBlockAlign(0),
|
fBlockAlign(0),
|
||||||
|
|
||||||
fStartTime(0),
|
fStartTime(0),
|
||||||
|
fOutputColorSpace(B_NO_COLOR_SPACE),
|
||||||
fOutputFrameCount(0),
|
fOutputFrameCount(0),
|
||||||
fOutputFrameRate(1.0),
|
fOutputFrameRate(1.0),
|
||||||
fOutputFrameSize(0),
|
fOutputFrameSize(0),
|
||||||
@@ -432,14 +432,24 @@ AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
|
|||||||
{
|
{
|
||||||
TRACE("AVCodecDecoder::_NegotiateVideoOutputFormat()\n");
|
TRACE("AVCodecDecoder::_NegotiateVideoOutputFormat()\n");
|
||||||
|
|
||||||
fOutputVideoFormat = fInputFormat.u.encoded_video.output;
|
TRACE(" requested video format 0x%x\n",
|
||||||
|
inOutFormat->u.raw_video.display.format);
|
||||||
|
|
||||||
fContext->width = fOutputVideoFormat.display.line_width;
|
// Make MediaPlayer happy (if not in rgb32 screen depth and no overlay,
|
||||||
fContext->height = fOutputVideoFormat.display.line_count;
|
// it will only ask for YCbCr, which DrawBitmap doesn't handle, so the
|
||||||
// fContext->frame_rate = (int)(fOutputVideoFormat.field_rate
|
// default colordepth is RGB32).
|
||||||
// * fContext->frame_rate_base);
|
if (inOutFormat->u.raw_video.display.format == B_YCbCr422)
|
||||||
|
fOutputColorSpace = B_YCbCr422;
|
||||||
|
else
|
||||||
|
fOutputColorSpace = B_RGB32;
|
||||||
|
|
||||||
fOutputFrameRate = fOutputVideoFormat.field_rate;
|
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
||||||
|
if (fSwsContext != NULL)
|
||||||
|
sws_freeContext(fSwsContext);
|
||||||
|
fSwsContext = NULL;
|
||||||
|
#else
|
||||||
|
fFormatConversionFunc = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
fContext->extradata = (uint8_t*)fExtraData;
|
fContext->extradata = (uint8_t*)fExtraData;
|
||||||
fContext->extradata_size = fExtraDataSize;
|
fContext->extradata_size = fExtraDataSize;
|
||||||
@@ -452,61 +462,29 @@ AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
|
|||||||
fContext->flags |= CODEC_FLAG_TRUNCATED;
|
fContext->flags |= CODEC_FLAG_TRUNCATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE(" requested video format 0x%x\n",
|
// close any previous instance
|
||||||
inOutFormat->u.raw_video.display.format);
|
if (fCodecInitDone) {
|
||||||
|
fCodecInitDone = false;
|
||||||
// Make MediaPlayer happy (if not in rgb32 screen depth and no overlay,
|
avcodec_close(fContext);
|
||||||
// it will only ask for YCbCr, which DrawBitmap doesn't handle, so the
|
|
||||||
// default colordepth is RGB32).
|
|
||||||
if (inOutFormat->u.raw_video.display.format == B_YCbCr422)
|
|
||||||
fOutputVideoFormat.display.format = B_YCbCr422;
|
|
||||||
else
|
|
||||||
fOutputVideoFormat.display.format = B_RGB32;
|
|
||||||
|
|
||||||
// Search for a pixel-format the codec handles
|
|
||||||
// TODO: We should try this a couple of times until it succeeds, each
|
|
||||||
// time using another pixel-format that is supported by the decoder.
|
|
||||||
// But libavcodec doesn't seem to offer any way to tell the decoder
|
|
||||||
// which format it should use.
|
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
|
||||||
if (fSwsContext != NULL)
|
|
||||||
sws_freeContext(fSwsContext);
|
|
||||||
fSwsContext = NULL;
|
|
||||||
#else
|
|
||||||
fFormatConversionFunc = 0;
|
|
||||||
#endif
|
|
||||||
// Iterate over supported codec formats
|
|
||||||
for (int i = 0; i < 1; i++) {
|
|
||||||
// close any previous instance
|
|
||||||
if (fCodecInitDone) {
|
|
||||||
fCodecInitDone = false;
|
|
||||||
avcodec_close(fContext);
|
|
||||||
}
|
|
||||||
// TODO: Set n-th fContext->pix_fmt here
|
|
||||||
if (avcodec_open2(fContext, fCodec, NULL) >= 0) {
|
|
||||||
fCodecInitDone = true;
|
|
||||||
|
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
|
||||||
fSwsContext = sws_getContext(fContext->width, fContext->height,
|
|
||||||
fContext->pix_fmt, fContext->width, fContext->height,
|
|
||||||
colorspace_to_pixfmt(fOutputVideoFormat.display.format),
|
|
||||||
SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
fFormatConversionFunc = resolve_colorspace(
|
|
||||||
fOutputVideoFormat.display.format, fContext->pix_fmt,
|
|
||||||
fContext->width, fContext->height);
|
|
||||||
}
|
|
||||||
if (fFormatConversionFunc != NULL)
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fCodecInitDone) {
|
if (avcodec_open2(fContext, fCodec, NULL) >= 0)
|
||||||
|
fCodecInitDone = true;
|
||||||
|
else {
|
||||||
TRACE("avcodec_open() failed to init codec!\n");
|
TRACE("avcodec_open() failed to init codec!\n");
|
||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ResetTempPacket();
|
||||||
|
|
||||||
|
status_t statusOfDecodingFirstFrame = _DecodeNextVideoFrame();
|
||||||
|
if (statusOfDecodingFirstFrame != B_OK) {
|
||||||
|
TRACE("[v] decoding first video frame failed\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: fSwsContext / fFormatConversionFunc should have been initialized
|
||||||
|
// by first call to _DecodeNextVideoFrame() above.
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
||||||
if (fSwsContext == NULL) {
|
if (fSwsContext == NULL) {
|
||||||
TRACE("No SWS Scale context or decoder has not set the pixel format "
|
TRACE("No SWS Scale context or decoder has not set the pixel format "
|
||||||
@@ -519,21 +497,25 @@ AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (fOutputVideoFormat.display.format == B_YCbCr422) {
|
|
||||||
fOutputVideoFormat.display.bytes_per_row
|
|
||||||
= 2 * fOutputVideoFormat.display.line_width;
|
|
||||||
} else {
|
|
||||||
fOutputVideoFormat.display.bytes_per_row
|
|
||||||
= 4 * fOutputVideoFormat.display.line_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
inOutFormat->type = B_MEDIA_RAW_VIDEO;
|
inOutFormat->type = B_MEDIA_RAW_VIDEO;
|
||||||
inOutFormat->u.raw_video = fOutputVideoFormat;
|
|
||||||
|
|
||||||
inOutFormat->require_flags = 0;
|
inOutFormat->require_flags = 0;
|
||||||
inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
|
inOutFormat->deny_flags = B_MEDIA_MAUI_UNDEFINED_FLAGS;
|
||||||
|
|
||||||
_ResetTempPacket();
|
inOutFormat->u.raw_video = fInputFormat.u.encoded_video.output;
|
||||||
|
|
||||||
|
inOutFormat->u.raw_video.interlace = 1;
|
||||||
|
// Progressive (non-interlaced) video frames are delivered
|
||||||
|
inOutFormat->u.raw_video.first_active = fHeader.u.raw_video.first_active_line;
|
||||||
|
inOutFormat->u.raw_video.last_active = fHeader.u.raw_video.line_count;
|
||||||
|
inOutFormat->u.raw_video.pixel_width_aspect = fHeader.u.raw_video.pixel_width_aspect;
|
||||||
|
inOutFormat->u.raw_video.pixel_height_aspect = fHeader.u.raw_video.pixel_height_aspect;
|
||||||
|
inOutFormat->u.raw_video.field_rate = fOutputFrameRate;
|
||||||
|
// Was calculated by first call to _DecodeNextVideoFrame()
|
||||||
|
|
||||||
|
inOutFormat->u.raw_video.display.format = fOutputColorSpace;
|
||||||
|
inOutFormat->u.raw_video.display.line_width = fHeader.u.raw_video.display_line_width;
|
||||||
|
inOutFormat->u.raw_video.display.line_count = fHeader.u.raw_video.display_line_count;
|
||||||
|
inOutFormat->u.raw_video.display.bytes_per_row = fHeader.u.raw_video.bytes_per_row;
|
||||||
|
|
||||||
#ifdef TRACE_AV_CODEC
|
#ifdef TRACE_AV_CODEC
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
@@ -713,7 +695,7 @@ AVCodecDecoder::_DecodeVideo(void* outBuffer, int64* outFrameCount,
|
|||||||
|
|
||||||
To every decoded video frame there is a media_header populated in
|
To every decoded video frame there is a media_header populated in
|
||||||
fHeader, containing the corresponding video frame properties.
|
fHeader, containing the corresponding video frame properties.
|
||||||
|
|
||||||
Normally every decoded video frame has a start_time field populated in the
|
Normally every decoded video frame has a start_time field populated in the
|
||||||
associated fHeader, that determines the presentation time of the frame.
|
associated fHeader, that determines the presentation time of the frame.
|
||||||
This relationship will only hold true, when each data chunk that is
|
This relationship will only hold true, when each data chunk that is
|
||||||
@@ -733,6 +715,12 @@ AVCodecDecoder::_DecodeVideo(void* outBuffer, int64* outFrameCount,
|
|||||||
and the start time it should be presented isn't established at the moment.
|
and the start time it should be presented isn't established at the moment.
|
||||||
Though this might change in the future.
|
Though this might change in the future.
|
||||||
|
|
||||||
|
More over the fOutputFrameRate variable is updated for every decoded video
|
||||||
|
frame.
|
||||||
|
|
||||||
|
On first call, the member variables fSwsContext / fFormatConversionFunc
|
||||||
|
are initialized.
|
||||||
|
|
||||||
\return B_OK, when we successfully decoded one video frame
|
\return B_OK, when we successfully decoded one video frame
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
@@ -842,6 +830,8 @@ AVCodecDecoder::_DecodeNextVideoFrame()
|
|||||||
_UpdateMediaHeaderForVideoFrame();
|
_UpdateMediaHeaderForVideoFrame();
|
||||||
_DeinterlaceAndColorConvertVideoFrame();
|
_DeinterlaceAndColorConvertVideoFrame();
|
||||||
|
|
||||||
|
ConvertAVCodecContextToVideoFrameRate(*fContext, fOutputFrameRate);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
dump_ffframe(fRawDecodedPicture, "ffpict");
|
dump_ffframe(fRawDecodedPicture, "ffpict");
|
||||||
// dump_ffframe(fPostProcessedDecodedPicture, "opict");
|
// dump_ffframe(fPostProcessedDecodedPicture, "opict");
|
||||||
@@ -909,8 +899,8 @@ AVCodecDecoder::_UpdateMediaHeaderForVideoFrame()
|
|||||||
fHeader.u.raw_video.display_line_width = fRawDecodedPicture->width;
|
fHeader.u.raw_video.display_line_width = fRawDecodedPicture->width;
|
||||||
fHeader.u.raw_video.display_line_count = fRawDecodedPicture->height;
|
fHeader.u.raw_video.display_line_count = fRawDecodedPicture->height;
|
||||||
fHeader.u.raw_video.bytes_per_row
|
fHeader.u.raw_video.bytes_per_row
|
||||||
= CalculateBytesPerRowWithColorSpaceAndVideoWidth(
|
= CalculateBytesPerRowWithColorSpaceAndVideoWidth(fOutputColorSpace,
|
||||||
fOutputVideoFormat.display.format, fRawDecodedPicture->width);
|
fRawDecodedPicture->width);
|
||||||
fHeader.u.raw_video.field_gamma = 1.0;
|
fHeader.u.raw_video.field_gamma = 1.0;
|
||||||
fHeader.u.raw_video.field_sequence = fFrame;
|
fHeader.u.raw_video.field_sequence = fFrame;
|
||||||
fHeader.u.raw_video.field_number = 0;
|
fHeader.u.raw_video.field_number = 0;
|
||||||
@@ -947,8 +937,8 @@ AVCodecDecoder::_UpdateMediaHeaderForVideoFrame()
|
|||||||
void
|
void
|
||||||
AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
|
AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
|
||||||
{
|
{
|
||||||
int width = fOutputVideoFormat.display.line_width;
|
int displayWidth = fHeader.u.raw_video.display_line_width;
|
||||||
int height = fOutputVideoFormat.display.line_count;
|
int displayHeight = fHeader.u.raw_video.display_line_count;
|
||||||
AVPicture deinterlacedPicture;
|
AVPicture deinterlacedPicture;
|
||||||
bool useDeinterlacedPicture = false;
|
bool useDeinterlacedPicture = false;
|
||||||
|
|
||||||
@@ -963,11 +953,11 @@ AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
|
|||||||
rawPicture.linesize[2] = fRawDecodedPicture->linesize[2];
|
rawPicture.linesize[2] = fRawDecodedPicture->linesize[2];
|
||||||
rawPicture.linesize[3] = fRawDecodedPicture->linesize[3];
|
rawPicture.linesize[3] = fRawDecodedPicture->linesize[3];
|
||||||
|
|
||||||
avpicture_alloc(&deinterlacedPicture,
|
avpicture_alloc(&deinterlacedPicture, fContext->pix_fmt, displayWidth,
|
||||||
fContext->pix_fmt, width, height);
|
displayHeight);
|
||||||
|
|
||||||
if (avpicture_deinterlace(&deinterlacedPicture, &rawPicture,
|
if (avpicture_deinterlace(&deinterlacedPicture, &rawPicture,
|
||||||
fContext->pix_fmt, width, height) < 0) {
|
fContext->pix_fmt, displayWidth, displayHeight) < 0) {
|
||||||
TRACE("[v] avpicture_deinterlace() - error\n");
|
TRACE("[v] avpicture_deinterlace() - error\n");
|
||||||
} else
|
} else
|
||||||
useDeinterlacedPicture = true;
|
useDeinterlacedPicture = true;
|
||||||
@@ -976,22 +966,20 @@ AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
|
|||||||
// Some decoders do not set pix_fmt until they have decoded 1 frame
|
// Some decoders do not set pix_fmt until they have decoded 1 frame
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
||||||
if (fSwsContext == NULL) {
|
if (fSwsContext == NULL) {
|
||||||
fSwsContext = sws_getContext(fContext->width, fContext->height,
|
fSwsContext = sws_getContext(displayWidth, displayHeight,
|
||||||
fContext->pix_fmt, fContext->width, fContext->height,
|
fContext->pix_fmt, displayWidth, displayHeight,
|
||||||
colorspace_to_pixfmt(fOutputVideoFormat.display.format),
|
colorspace_to_pixfmt(fOutputColorSpace),
|
||||||
SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (fFormatConversionFunc == NULL) {
|
if (fFormatConversionFunc == NULL) {
|
||||||
fFormatConversionFunc = resolve_colorspace(
|
fFormatConversionFunc = resolve_colorspace(fOutputColorSpace,
|
||||||
fOutputVideoFormat.display.format, fContext->pix_fmt,
|
fContext->pix_fmt, displayWidth, displayHeight);
|
||||||
fContext->width, fContext->height);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
fDecodedDataSizeInBytes = avpicture_get_size(
|
fDecodedDataSizeInBytes = avpicture_get_size(
|
||||||
colorspace_to_pixfmt(fOutputVideoFormat.display.format),
|
colorspace_to_pixfmt(fOutputColorSpace), displayWidth, displayHeight);
|
||||||
fContext->width, fContext->height);
|
|
||||||
|
|
||||||
if (fDecodedData == NULL)
|
if (fDecodedData == NULL)
|
||||||
fDecodedData
|
fDecodedData
|
||||||
@@ -999,7 +987,7 @@ AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
|
|||||||
|
|
||||||
fPostProcessedDecodedPicture->data[0] = fDecodedData;
|
fPostProcessedDecodedPicture->data[0] = fDecodedData;
|
||||||
fPostProcessedDecodedPicture->linesize[0]
|
fPostProcessedDecodedPicture->linesize[0]
|
||||||
= fOutputVideoFormat.display.bytes_per_row;
|
= fHeader.u.raw_video.bytes_per_row;
|
||||||
|
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
||||||
if (fSwsContext != NULL) {
|
if (fSwsContext != NULL) {
|
||||||
@@ -1023,22 +1011,22 @@ AVCodecDecoder::_DeinterlaceAndColorConvertVideoFrame()
|
|||||||
|
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
||||||
sws_scale(fSwsContext, deinterlacedFrame.data,
|
sws_scale(fSwsContext, deinterlacedFrame.data,
|
||||||
deinterlacedFrame.linesize, 0, fContext->height,
|
deinterlacedFrame.linesize, 0, displayHeight,
|
||||||
fPostProcessedDecodedPicture->data,
|
fPostProcessedDecodedPicture->data,
|
||||||
fPostProcessedDecodedPicture->linesize);
|
fPostProcessedDecodedPicture->linesize);
|
||||||
#else
|
#else
|
||||||
(*fFormatConversionFunc)(&deinterlacedFrame,
|
(*fFormatConversionFunc)(&deinterlacedFrame,
|
||||||
fPostProcessedDecodedPicture, width, height);
|
fPostProcessedDecodedPicture, displayWidth, displayHeight);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
#if USE_SWS_FOR_COLOR_SPACE_CONVERSION
|
||||||
sws_scale(fSwsContext, fRawDecodedPicture->data,
|
sws_scale(fSwsContext, fRawDecodedPicture->data,
|
||||||
fRawDecodedPicture->linesize, 0, fContext->height,
|
fRawDecodedPicture->linesize, 0, displayHeight,
|
||||||
fPostProcessedDecodedPicture->data,
|
fPostProcessedDecodedPicture->data,
|
||||||
fPostProcessedDecodedPicture->linesize);
|
fPostProcessedDecodedPicture->linesize);
|
||||||
#else
|
#else
|
||||||
(*fFormatConversionFunc)(fRawDecodedPicture,
|
(*fFormatConversionFunc)(fRawDecodedPicture,
|
||||||
fPostProcessedDecodedPicture, width, height);
|
fPostProcessedDecodedPicture, displayWidth, displayHeight);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ private:
|
|||||||
// decoded audio / video frame
|
// decoded audio / video frame
|
||||||
|
|
||||||
media_format fInputFormat;
|
media_format fInputFormat;
|
||||||
media_raw_video_format fOutputVideoFormat;
|
|
||||||
|
|
||||||
int64 fFrame;
|
int64 fFrame;
|
||||||
bool fIsAudio;
|
bool fIsAudio;
|
||||||
@@ -98,6 +97,7 @@ private:
|
|||||||
int fBlockAlign;
|
int fBlockAlign;
|
||||||
|
|
||||||
bigtime_t fStartTime;
|
bigtime_t fStartTime;
|
||||||
|
color_space fOutputColorSpace;
|
||||||
int32 fOutputFrameCount;
|
int32 fOutputFrameCount;
|
||||||
float fOutputFrameRate;
|
float fOutputFrameRate;
|
||||||
int fOutputFrameSize;
|
int fOutputFrameSize;
|
||||||
|
|||||||
Reference in New Issue
Block a user