FFMPEG Plugin: Fix playing video files.

- This should fix the bug where video files that played well before the recent
  changes to the FFMPEG Plugin didn't play anymore. Now we apply the essential
  video container properties (that were passed by with Setup()) to the
  AVCodecContext. Some video formats simply store those properties in the
  container only (e.g. AVI, WMV) and not in the video frames itself
  (e.g. MPEG2).
  Tested with several files from samples.ffmpeg.org and from the FATE suite of
  FFMPEG.
This commit is contained in:
Colin Günther 2014-08-20 11:00:43 +02:00
parent d78faaaf70
commit 75bd62e868
3 changed files with 131 additions and 0 deletions

View File

@ -443,6 +443,14 @@ AVCodecDecoder::_NegotiateVideoOutputFormat(media_format* inOutFormat)
TRACE(" requested video format 0x%x\n",
inOutFormat->u.raw_video.display.format);
_ApplyEssentialVideoContainerPropertiesToContext();
// This makes video formats play that encode the video properties in
// the video container (e.g. WMV) and not in the video frames
// themself (e.g. MPEG2).
// Note: Doing this step everytime is OK, because the first call to
// _DecodeNextVideoFrame() will update the essential video format
// properties accordingly.
// Make MediaPlayer happy (if not in rgb32 screen depth and no overlay,
// it will only ask for YCbCr, which DrawBitmap doesn't handle, so the
// default colordepth is RGB32).
@ -822,6 +830,44 @@ AVCodecDecoder::_DecodeNextVideoFrame()
}
/*! \brief Applies all essential video input properties from fInputFormat to
fContext.
Note: This function must be called before the AVCodec is opened via
avcodec_open2(). Otherwise the behaviour of FFMPEG's video decoding
function avcodec_decode_video2() is undefined.
Essential properties applied:
- display.line_width copied to fContext->width
- display.line_count copied to fContext->height
- pixel_width_aspect and pixel_height_aspect converted to
fContext->sample_aspect_ratio
- field_rate converted to fContext->time_base and
fContext->ticks_per_frame
*/
void
AVCodecDecoder::_ApplyEssentialVideoContainerPropertiesToContext()
{
media_raw_video_format containerProperties
= fInputFormat.u.encoded_video.output;
fContext->width = containerProperties.display.line_width;
fContext->height = containerProperties.display.line_count;
if (containerProperties.pixel_width_aspect > 0
&& containerProperties.pixel_height_aspect > 0) {
ConvertVideoAspectWidthAndHeightToAVCodecContext(
containerProperties.pixel_width_aspect,
containerProperties.pixel_height_aspect, *fContext);
}
if (containerProperties.field_rate > 0.0) {
ConvertVideoFrameRateToAVCodecContext(containerProperties.field_rate,
*fContext);
}
}
/*! \brief Loads the next video chunk into fVideoChunkBuffer and assigns it
(including the start time) to fTempPacket accordingly only if
fTempPacket is empty.

View File

@ -61,6 +61,7 @@ private:
media_header* mediaHeader,
media_decode_info* info);
status_t _DecodeNextVideoFrame();
void _ApplyEssentialVideoContainerPropertiesToContext();
status_t _LoadNextVideoChunkIfNeededAndAssignStartTime();
// TODO: Remove the "Video" word once
// the audio path is responsible for

View File

@ -24,6 +24,9 @@ extern "C" {
/*! \brief Converts FFmpeg notation of video aspect ratio into the Media Kits
notation.
\see ConvertVideoAspectWidthAndHeightToAVCodecContext() for converting in
the other direction.
\param contextIn An AVCodeContext structure of FFmpeg containing the values
needed to calculate the Media Kit video aspect ratio.
The following fields are used for the calculation:
@ -72,6 +75,57 @@ ConvertAVCodecContextToVideoAspectWidthAndHeight(AVCodecContext& contextIn,
}
/*! \brief Converts the Media Kits notation of video aspect ratio into FFmpegs
notation.
\see ConvertAVCodecContextToVideoAspectWidthAndHeight() for converting in
the other direction.
\param pixelWidthAspectIn Contains Media Kits notation of the video aspect
ratio width. E.g. 16:9 -> 16 is passed here.
\param pixelHeightAspectIn Contains Media Kits notation of the video aspect
ratio height. E.g. 16:9 -> 9 is passed here.
\param contextInOut An AVCodecContext structure of FFmpeg.
On input must contain the following fields already initialized
otherwise the behaviour is undefined:
- AVCodecContext.width (must)
- AVCodecContext.height (must)
On output contains converted values in the following fields (other
fields stay as they were on input):
- AVCodecContext.sample_aspect_ratio.num
- AVCodecContext.sample_aspect_ratio.den
*/
inline void
ConvertVideoAspectWidthAndHeightToAVCodecContext(uint16 pixelWidthAspectIn,
uint16 pixelHeightAspectIn, AVCodecContext& contextInOut)
{
assert(pixelWidthAspectIn > 0);
assert(pixelHeightAspectIn > 0);
assert(contextInOut.width > 0);
assert(contextInOut.height > 0);
AVRational pureVideoDimensionAspectRatio;
av_reduce(&pureVideoDimensionAspectRatio.num,
&pureVideoDimensionAspectRatio.den, contextInOut.width,
contextInOut.height, 1024 * 1024);
if (pureVideoDimensionAspectRatio.num == pixelWidthAspectIn
&& pureVideoDimensionAspectRatio.den == pixelHeightAspectIn) {
// The passed Media Kit pixel aspect ratio equals the video dimension
// aspect ratio. Set sample_aspect_ratio to "ignore".
contextInOut.sample_aspect_ratio.num = 0;
contextInOut.sample_aspect_ratio.den = 1;
return;
}
av_reduce(&contextInOut.sample_aspect_ratio.num,
&contextInOut.sample_aspect_ratio.den,
contextInOut.height * pixelWidthAspectIn,
contextInOut.width * pixelHeightAspectIn,
1024 * 1024);
}
/*! \brief Calculates bytes per row for a video frame.
\param colorSpace The Media Kit color space the video frame uses.
@ -108,6 +162,9 @@ CalculateBytesPerRowWithColorSpaceAndVideoWidth(color_space colorSpace, int vide
/*! \brief Converts FFmpeg notation of video frame rate into the Media Kits
notation.
\see ConvertAVCodecContextToVideoFrameRate() for converting in the other
direction.
\param contextIn An AVCodeContext structure of FFmpeg containing the values
needed to calculate the Media Kit video frame rate.
The following fields are used for the calculation:
@ -134,4 +191,31 @@ ConvertAVCodecContextToVideoFrameRate(AVCodecContext& contextIn, float& frameRat
= possiblyInterlacedFrameRate / numberOfInterlacedFramesPerFullFrame;
}
/*! \brief Converts the Media Kits notation of video frame rate to FFmpegs
notation.
\see ConvertAVCodecContextToVideoFrameRate() for converting in the other
direction.
\param frameRateIn Contains Media Kits notation of the video frame rate
that will be converted into FFmpegs notation. Must be greater than
zero.
\param contextOut An AVCodecContext structure of FFmpeg.
On output contains converted values in the following fields (other
fields stay as they were on input):
- AVCodecContext.time_base.num
- AVCodecContext.time_base.den
- AVCodecContext.ticks_per_frame is set to 1
*/
inline void
ConvertVideoFrameRateToAVCodecContext(float frameRateIn,
AVCodecContext& contextOut)
{
assert(frameRateIn > 0);
contextOut.ticks_per_frame = 1;
contextOut.time_base = av_d2q(1.0 / frameRateIn, 1024);
}
#endif // UTILITIES_H