/* * Copyright 2009, Stephan Aßmus * Copyright 2014, Colin Günther * Copyright 2018, Dario Casalinuovo * All rights reserved. Distributed under the terms of the GNU L-GPL license. */ #ifndef UTILITIES_H #define UTILITIES_H /*! \brief This file contains functions to convert and calculate values from FFmpeg to Media Kit and vice versa. */ #include #include #include extern "C" { #include "avcodec.h" } /*! \brief Structure used for passing AVPacket metadata through media_header::user_data. */ struct avpacket_user_data { int64_t pts; int64_t dts; int stream_index; int flags; int64_t duration; int64_t pos; }; #define AVPACKET_USER_DATA_TYPE 'ffav' /*! \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: - AVCodecContext.sample_aspect_ratio.num (optional) - AVCodecContext.sample_aspect_ratio.den (optional) - AVCodecContext.width (must) - AVCodecContext.height (must) \param pixelWidthAspectOut On return contains Media Kits notation of the video aspect ratio width. E.g. 16:9 -> 16 is returned here \param pixelHeightAspectOut On return contains Media Kits notation of the video aspect ratio height. E.g. 16:9 -> 9 is returned here */ inline void ConvertAVCodecContextToVideoAspectWidthAndHeight(AVCodecContext& contextIn, uint16& pixelWidthAspectOut, uint16& pixelHeightAspectOut) { if (contextIn.width <= 0 || contextIn.height <= 0) { fprintf(stderr, "Cannot compute video aspect ratio correctly\n"); pixelWidthAspectOut = 1; pixelHeightAspectOut = 1; return; } assert(contextIn.sample_aspect_ratio.num >= 0); AVRational pixelAspectRatio; if (contextIn.sample_aspect_ratio.num == 0 || contextIn.sample_aspect_ratio.den == 0) { // AVCodecContext doesn't contain a video aspect ratio, so calculate it // ourselve based solely on the video dimensions av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, contextIn.width, contextIn.height, 1024 * 1024); pixelWidthAspectOut = static_cast(pixelAspectRatio.num); pixelHeightAspectOut = static_cast(pixelAspectRatio.den); return; } // AVCodecContext contains a video aspect ratio, so use it av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, contextIn.width * contextIn.sample_aspect_ratio.num, contextIn.height * contextIn.sample_aspect_ratio.den, 1024 * 1024); pixelWidthAspectOut = static_cast(pixelAspectRatio.num); pixelHeightAspectOut = static_cast(pixelAspectRatio.den); } inline void ConvertAVCodecParametersToVideoAspectWidthAndHeight(AVCodecParameters& parametersIn, uint16& pixelWidthAspectOut, uint16& pixelHeightAspectOut) { if (parametersIn.width <= 0 || parametersIn.height <= 0) { fprintf(stderr, "Cannot compute video aspect ratio correctly\n"); pixelWidthAspectOut = 1; pixelHeightAspectOut = 1; return; } assert(parametersIn.sample_aspect_ratio.num >= 0); AVRational pixelAspectRatio; if (parametersIn.sample_aspect_ratio.num == 0 || parametersIn.sample_aspect_ratio.den == 0) { // AVCodecContext doesn't contain a video aspect ratio, so calculate it // ourselve based solely on the video dimensions av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, parametersIn.width, parametersIn.height, 1024 * 1024); pixelWidthAspectOut = static_cast(pixelAspectRatio.num); pixelHeightAspectOut = static_cast(pixelAspectRatio.den); return; } // AVCodecContext contains a video aspect ratio, so use it av_reduce(&pixelAspectRatio.num, &pixelAspectRatio.den, parametersIn.width * parametersIn.sample_aspect_ratio.num, parametersIn.height * parametersIn.sample_aspect_ratio.den, 1024 * 1024); pixelWidthAspectOut = static_cast(pixelAspectRatio.num); pixelHeightAspectOut = static_cast(pixelAspectRatio.den); } /*! \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) { if (contextInOut.width <= 0 || contextInOut.height <= 0) { fprintf(stderr, "Cannot compute video aspect ratio correctly\n"); // We can't do anything, set the aspect ratio to 'ignore'. contextInOut.sample_aspect_ratio.num = 0; contextInOut.sample_aspect_ratio.den = 1; return; } assert(pixelWidthAspectIn > 0); assert(pixelHeightAspectIn > 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. \param videoWidth The width of the video frame. \returns bytes per video frame row \returns Zero, when bytes per video frame cannot be calculated. */ inline uint32 CalculateBytesPerRowWithColorSpaceAndVideoWidth(color_space colorSpace, int videoWidth) { assert(videoWidth >= 0); const uint32 kBytesPerRowUnknown = 0; size_t pixelChunk; size_t rowAlignment; size_t pixelsPerChunk; if (get_pixel_size_for(colorSpace, &pixelChunk, &rowAlignment, &pixelsPerChunk) != B_OK) return kBytesPerRowUnknown; uint32 bytesPerRow = pixelChunk * videoWidth / pixelsPerChunk; uint32 numberOfUnalignedBytes = bytesPerRow % rowAlignment; if (numberOfUnalignedBytes == 0) return bytesPerRow; uint32 numberOfBytesNeededForAlignment = rowAlignment - numberOfUnalignedBytes; bytesPerRow += numberOfBytesNeededForAlignment; return bytesPerRow; } /*! \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.framerate.num - AVCodecContext.framerate.den */ inline void ConvertVideoFrameRateToAVCodecContext(float frameRateIn, AVCodecContext& contextOut) { assert(frameRateIn > 0); contextOut.framerate = av_d2q(frameRateIn, 1024); contextOut.time_base = av_d2q(1.0 / frameRateIn, 1024); } /*! \brief Converts the Media Kits notation of an audio sample format to FFmpegs notation. \see ConvertAVSampleFormatToRawAudioFormat() for converting in the other direction. \param rawAudioFormatIn Contains Media Kits notation of an audio sample format that will be converted into FFmpegs notation. \param sampleFormatOut On output contains FFmpegs notation of the passed audio sample format. Might return AV_SAMPLE_FMT_NONE if there is no conversion path. */ inline void ConvertRawAudioFormatToAVSampleFormat(uint32 rawAudioFormatIn, AVSampleFormat& sampleFormatOut) { switch (rawAudioFormatIn) { case media_raw_audio_format::B_AUDIO_FLOAT: sampleFormatOut = AV_SAMPLE_FMT_FLT; return; case media_raw_audio_format::B_AUDIO_DOUBLE: sampleFormatOut = AV_SAMPLE_FMT_DBL; return; case media_raw_audio_format::B_AUDIO_INT: sampleFormatOut = AV_SAMPLE_FMT_S32; return; case media_raw_audio_format::B_AUDIO_SHORT: sampleFormatOut = AV_SAMPLE_FMT_S16; return; case media_raw_audio_format::B_AUDIO_UCHAR: sampleFormatOut = AV_SAMPLE_FMT_U8; return; default: // Silence compiler warnings about unhandled enumeration values. break; } sampleFormatOut = AV_SAMPLE_FMT_NONE; } /*! \brief Converts FFmpegs notation of an audio sample format to the Media Kits notation. \see ConvertAVSampleFormatToRawAudioFormat() for converting in the other direction. \param sampleFormatIn Contains FFmpegs notation of an audio sample format that will be converted into the Media Kits notation. \param rawAudioFormatOut On output contains Media Kits notation of the passed audio sample format. Might return 0 if there is no conversion path. */ inline void ConvertAVSampleFormatToRawAudioFormat(AVSampleFormat sampleFormatIn, uint32& rawAudioFormatOut) { switch (sampleFormatIn) { case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP: rawAudioFormatOut = media_raw_audio_format::B_AUDIO_FLOAT; return; case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBLP: rawAudioFormatOut = media_raw_audio_format::B_AUDIO_DOUBLE; return; case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: rawAudioFormatOut = media_raw_audio_format::B_AUDIO_INT; return; case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: rawAudioFormatOut = media_raw_audio_format::B_AUDIO_SHORT; return; case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: rawAudioFormatOut = media_raw_audio_format::B_AUDIO_UCHAR; return; default: // Silence compiler warnings about unhandled enumeration values. break; } const uint32 kBAudioNone = 0; rawAudioFormatOut = kBAudioNone; } #endif // UTILITIES_H