Search code examples
c++cffmpeglibav

Using explicit casting to solve invalid conversion from ‘int’ to ‘AVRounding’


I'm trying to follow the "learn ffmpeg the hard way" guide on github (https://github.com/leandromoreira/ffmpeg-libav-tutorial#chapter-2---remuxing) and when I try to run the remuxing code for myself using a C++ compiler I always get the error

invalid conversion from ‘int’ to ‘AVRounding’[-fpermissive]

The only real difference between my code and the github original is that I take the input filename and output filename as parameters instead. Doing some resarch I have found that AVRounding is a enum? I think? Thus I need to explicitly cast somehow. However I'm failing to understand how explicit casting works and how I would use it to solve my issue. Where would I cast it? Here's the line that causes the issue:

packet.pts =  av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
packet.dts =  av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);

Could anyone explain how to solve the issue? Sorry if it's a beginners mistake. Thanks in advance for any help.


Solution

  • AVRounding is indeed an enum (to be exact, an unscoped enum without a fixed underlying type):

    /**
     * Rounding methods.
     */
    enum AVRounding {
        AV_ROUND_ZERO     = 0, ///< Round toward zero.
        AV_ROUND_INF      = 1, ///< Round away from zero.
        AV_ROUND_DOWN     = 2, ///< Round toward -infinity.
        AV_ROUND_UP       = 3, ///< Round toward +infinity.
        AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero.
        /**
         * Flag telling rescaling functions to pass `INT64_MIN`/`MAX` through
         * unchanged, avoiding special cases for #AV_NOPTS_VALUE.
         *
         * Unlike other values of the enumeration AVRounding, this value is a
         * bitmask that must be used in conjunction with another value of the
         * enumeration through a bitwise OR, in order to set behavior for normal
         * cases.
         *
         * @code{.c}
         * av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
         * // Rescaling 3:
         * //     Calculating 3 * 1 / 2
         * //     3 / 2 is rounded up to 2
         * //     => 2
         *
         * av_rescale_rnd(AV_NOPTS_VALUE, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
         * // Rescaling AV_NOPTS_VALUE:
         * //     AV_NOPTS_VALUE == INT64_MIN
         * //     AV_NOPTS_VALUE is passed through
         * //     => AV_NOPTS_VALUE
         * @endcode
         */
        AV_ROUND_PASS_MINMAX = 8192,
    };
    

    The values you are trying to pass to av_rescale_q_rnd() are not themselves ints, but they are implicitly convertible to int. However, C++ does not allow an int to be implicitly converted to a enum, and FFmpeg does not define an operator| for AVRounding in C++, so the result of OR'ing the values of AV_ROUND_NEAR_INF and AV_ROUND_PASS_MINMAX together actually does produce an int, hence the need for a type-cast to go back to an AVRounding, eg:

    packet.pts =  av_rescale_q_rnd(..., static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
    packet.dts =  av_rescale_q_rnd(..., static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
    

    You can manually define an operator| to avoid that type-casting at the call site, if you wish, eg:

    inline AVRounding operator|(AVRounding a, AVRounding b)
    {
        return static_cast<AVRounding>(static_cast<int>(a) | static_cast<int>(b));
    }
    
    ...
    
    packet.pts =  av_rescale_q_rnd(..., AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
    packet.dts =  av_rescale_q_rnd(..., AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);