FFmpeg有理数相关的源码:AVRational结构体和其相关的函数分析

一、引言

有理数是整数(正整数、0、负整数)和分数的统称,是整数和分数的集合。整数也可看作是分母是1的分数。不是有理数的实数称为无理数,即无理数的小数部分是无限不循环的数。

AVRational是FFmpeg源码中的一个结构体,用来抽象有理数。为什么不使用double类型(浮点数)呢?虽然有理数可以表示为浮点数,但浮点操作的转换过程是一个有损过程,使用浮点数计算可能会导致有理数出现精度误差;另一方面,FFmpeg的特性要求高精度的时间戳计算。AVRational结构体和其相关函数可以作为一个通用接口,用于操作作为分子和分母对的有理数。

AVRational相关的函数都带有`_q`后缀,表示数学符号"ℚ"(Q),表示所有有理数的集合。

二、AVRational结构体的声明

AVRational结构体定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavutil/rational.h中:

cpp 复制代码
/**
 * Rational number (pair of numerator and denominator).
 */
typedef struct AVRational{
    int num; ///< Numerator
    int den; ///< Denominator
} AVRational;

FFmpeg源码中使用AVRational结构体来表示有理数。

成员变量num:有理数的分子

成员变量den:有理数的分母

三、av_make_q函数的定义

av_make_q函数定义在libavutil/rational.h中:

cpp 复制代码
/**
 * Create an AVRational.
 *
 * Useful for compilers that do not support compound literals.
 *
 * @note The return value is not reduced.
 * @see av_reduce()
 */
static inline AVRational av_make_q(int num, int den)
{
    AVRational r = { num, den };
    return r;
}

其作用是:给有理数的分子和分母赋值并返回。

形参num:输入型参数。传递给有理数分子的值。执行av_make_q函数后,有理数分子的值为num。

形参den:输入型参数。传递给有理数分母的值。执行av_make_q函数后,有理数分母的值为den。

返回值:返回被赋值的AVRational类型的有理数。

四、av_cmp_q函数的定义

(一)av_cmp_q函数的定义

av_cmp_q函数定义在libavutil/rational.h中:

cpp 复制代码
/**
 * Compare two rationals.
 *
 * @param a First rational
 * @param b Second rational
 *
 * @return One of the following values:
 *         - 0 if `a == b`
 *         - 1 if `a > b`
 *         - -1 if `a < b`
 *         - `INT_MIN` if one of the values is of the form `0 / 0`
 */
static inline int av_cmp_q(AVRational a, AVRational b){
    const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den;

    if(tmp) return (int)((tmp ^ a.den ^ b.den)>>63)|1;
    else if(b.den && a.den) return 0;
    else if(a.num && b.num) return (a.num>>31) - (b.num>>31);
    else                    return INT_MIN;
}

其作用是:比较两个有理数的大小。

形参a:输入型参数。被比较的第一个有理数。

形参b:输入型参数。被比较的第二个有理数。

返回值:如果a的值等于b,返回0;如果a>b,返回1;如果a<b,返回-1;如果a或b中有一个有理数的分子和分母同时为0,返回`INT_MIN`(-2147483648)。注意:这函数非常奇怪,如果a或b中有一个有理数的分子不为0但分母为0,这函数不会返回`INT_MIN`;只有当a或b中有一个有理数的分子和分母同时为0,才会返回`INT_MIN`。我们都知道有理数的分母是不能等于零的,分母等于零是没有意义的。av_cmp_q函数内部没有对a和b中分子不为0但分母为0的情况进行处理。所以在使用该函数之前最好要先在其外部判断a和b分母是否为0,避免被比较的有理数分母出现0的情况。

(二)av_cmp_q函数的使用例子

编写测试例子main.c,在Ubuntu中使用9.4.0版本的gcc编译通过:

cpp 复制代码
#include <stdio.h>
#include <stdint.h>
#include <limits.h>

typedef struct AVRational{
    int num; ///< Numerator
    int den; ///< Denominator
} AVRational;


static inline AVRational av_make_q(int num, int den)
{
    AVRational r = { num, den };
    return r;
}


static inline int av_cmp_q(AVRational a, AVRational b){
    const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den;

    if(tmp) return (int)((tmp ^ a.den ^ b.den)>>63)|1;
    else if(b.den && a.den) return 0;
    else if(a.num && b.num) return (a.num>>31) - (b.num>>31);
    else                    return INT_MIN;
}


int main()
{
    AVRational a = av_make_q(1, 2);
    AVRational b = av_make_q(1, 3);
    printf("av_cmp_q(a, b): %d\n", av_cmp_q(a, b));

    AVRational c = av_make_q(65536, 1);
    AVRational d = av_make_q(2, 0);
    printf("av_cmp_q(c, d): %d\n", av_cmp_q(c, d));

    AVRational e = av_make_q(0, 0);
    AVRational f = av_make_q(1, 3);
    printf("av_cmp_q(e, f): %d\n", av_cmp_q(e, f));

    return 0;
}

输出如下:

五、av_q2d函数的定义

av_q2d函数定义在libavutil/rational.h中:

cpp 复制代码
/**
 * Convert an AVRational to a `double`.
 * @param a AVRational to convert
 * @return `a` in floating-point form
 * @see av_d2q()
 */
static inline double av_q2d(AVRational a){
    return a.num / (double) a.den;
}

该函数作用是:将有理数从AVRational类型转换为double类型(双精度浮点数)。该操作可能会造成精度丢失。

形参a:输入型参数。被转换的有理数。

返回值:由形参a转换出来的double类型数值。

六、av_reduce函数的定义

av_reduce函数可以用来计算视频帧率。关于该函数的用法可以参考:《音视频入门基础:H.264专题(15)------FFmpeg源码中通过SPS属性获取视频帧率的实现》。

七、av_mul_q函数的定义

av_mul_q函数定义在libavutil/rational.c中:

cpp 复制代码
/**
 * Multiply two rationals.
 * @param b First rational
 * @param c Second rational
 * @return b*c
 */
AVRational av_mul_q(AVRational b, AVRational c)
{
    av_reduce(&b.num, &b.den,
               b.num * (int64_t) c.num,
               b.den * (int64_t) c.den, INT_MAX);
    return b;
}

该函数作用是:将有理数b * c的结果作为返回值返回。

八、av_div_q函数的定义

av_div_q函数定义在libavutil/rational.c中:

cpp 复制代码
/**
 * Divide one rational by another.
 * @param b First rational
 * @param c Second rational
 * @return b/c
 */
AVRational av_div_q(AVRational b, AVRational c)
{
    return av_mul_q(b, (AVRational) { c.den, c.num });
}

该函数作用是:将有理数b ÷ c的结果作为返回值返回。

九、av_add_q函数的定义

av_add_q函数定义在libavutil/rational.c中:

cpp 复制代码
/**
 * Add two rationals.
 * @param b First rational
 * @param c Second rational
 * @return b+c
 */
AVRational av_add_q(AVRational b, AVRational c) {
    av_reduce(&b.num, &b.den,
               b.num * (int64_t) c.den +
               c.num * (int64_t) b.den,
               b.den * (int64_t) c.den, INT_MAX);
    return b;
}

该函数作用是:将有理数b + c的结果作为返回值返回。

十、av_sub_q函数的定义

av_sub_q函数定义在libavutil/rational.c中:

cpp 复制代码
/**
 * Subtract one rational from another.
 * @param b First rational
 * @param c Second rational
 * @return b-c
 */
AVRational av_sub_q(AVRational b, AVRational c)
{
    return av_add_q(b, (AVRational) { -c.num, c.den });
}

该函数作用是:将有理数b - c的结果作为返回值返回。

相关推荐
EasyCVR8 小时前
EHOME视频平台EasyCVR视频融合平台使用OBS进行RTMP推流,WebRTC播放出现抖动、卡顿如何解决?
人工智能·算法·ffmpeg·音视频·webrtc·监控视频接入
简鹿办公9 小时前
使用 FFmpeg 进行音视频转换的相关命令行参数解释
ffmpeg·简鹿视频格式转换器·ffmpeg视频转换
EasyCVR12 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
runing_an_min12 小时前
ffmpeg 视频滤镜:屏蔽边框杂色- fillborders
ffmpeg·音视频·fillborders
岁月小龙1 天前
如何让ffmpeg运行时从当前目录加载库,而不是从/lib64
ffmpeg·origin·ffprobe·rpath
行者记2 天前
ffmpeg命令——从wireshark包中的rtp包中分离h264
测试工具·ffmpeg·wireshark
EasyCVR2 天前
国标GB28181视频平台EasyCVR私有化视频平台工地防盗视频监控系统方案
运维·科技·ffmpeg·音视频·1024程序员节·监控视频接入
hypoqqq2 天前
使用ffmpeg播放rtsp视频流
ffmpeg
cuijiecheng20182 天前
音视频入门基础:FLV专题(24)——FFmpeg源码中,获取FLV文件视频信息的实现
ffmpeg·音视频
QMCY_jason2 天前
黑豹X2 armbian 编译rkmpp ffmpeg 实现CPU视频转码
ffmpeg