diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp index 73bb01f7e9..28cc577567 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.cpp @@ -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. diff --git a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h index 516bfc3d1c..8f00b9513e 100644 --- a/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h +++ b/src/add-ons/media/plugins/ffmpeg/AVCodecDecoder.h @@ -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 diff --git a/src/add-ons/media/plugins/ffmpeg/Utilities.h b/src/add-ons/media/plugins/ffmpeg/Utilities.h index f51f6b2bfe..bf01e4818b 100644 --- a/src/add-ons/media/plugins/ffmpeg/Utilities.h +++ b/src/add-ons/media/plugins/ffmpeg/Utilities.h @@ -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