欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > FFmpeg源码:av_reduce函数分析

FFmpeg源码:av_reduce函数分析

2025/4/19 10:18:37 来源:https://blog.csdn.net/u014552102/article/details/140885932  浏览:    关键词:FFmpeg源码:av_reduce函数分析

 =================================================================

AVRational结构体和其相关的函数分析:

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

FFmpeg源码:av_reduce函数分析

 =================================================================

一、av_reduce函数的声明

av_reduce函数声明在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的头文件libavutil/rational.h中:

/*** Reduce a fraction.** This is useful for framerate calculations.** @param[out] dst_num Destination numerator* @param[out] dst_den Destination denominator* @param[in]      num Source numerator* @param[in]      den Source denominator* @param[in]      max Maximum allowed values for `dst_num` & `dst_den`* @return 1 if the operation is exact, 0 otherwise*/
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max);

该函数作用是:化简有理数(一般用来化简分数)。比如分数4/6(六分之四),化简后为2/3(三分之二)。需要对AVRational结构体进行加减乘除(四则运算)时会调用该函数;av_reduce函数也可以用来计算视频帧率。具体可以参考:《音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现》。

形参dst_num:输出型参数。执行av_reduce函数后,dst_num指向的整形变量值会变为“被化简后的有理数中的分子”。

形参dst_den:输出型参数。执行av_reduce函数后,dst_den指向的整形变量值会变为“被化简后的有理数中的分母”。

形参num:输入型参数。需要被化简的有理数中的分子。

形参den:输入型参数。需要被化简的有理数中的分母。

形参max:输入型参数,用于进行限制。被化简后的有理数中的分子和分母的绝对值都不能超过该值。

返回值:1:化简结果是准确的;0:化简结果不准确。

二、av_reduce函数的定义

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

int av_reduce(int *dst_num, int *dst_den,int64_t num, int64_t den, int64_t max)
{AVRational a0 = { 0, 1 }, a1 = { 1, 0 };int sign = (num < 0) ^ (den < 0);int64_t gcd = av_gcd(FFABS(num), FFABS(den));if (gcd) {num = FFABS(num) / gcd;den = FFABS(den) / gcd;}if (num <= max && den <= max) {a1 = (AVRational) { num, den };den = 0;}while (den) {uint64_t x        = num / den;int64_t next_den  = num - den * x;int64_t a2n       = x * a1.num + a0.num;int64_t a2d       = x * a1.den + a0.den;if (a2n > max || a2d > max) {if (a1.num) x =          (max - a0.num) / a1.num;if (a1.den) x = FFMIN(x, (max - a0.den) / a1.den);if (den * (2 * x * a1.den + a0.den) > num * a1.den)a1 = (AVRational) { x * a1.num + a0.num, x * a1.den + a0.den };break;}a0  = a1;a1  = (AVRational) { a2n, a2d };num = den;den = next_den;}av_assert2(av_gcd(a1.num, a1.den) <= 1U);av_assert2(a1.num <= max && a1.den <= max);*dst_num = sign ? -a1.num : a1.num;*dst_den = a1.den;return den == 0;
}

三、av_reduce函数的内部实现分析

av_reduce函数中,首先通过异或(^)运算符判断输入的有理数的正负。变量sign的值为0:输入的有理数为正数;sign值为1:输入的有理数为负数或0;

int sign = (num < 0) ^ (den < 0);

然后通过av_gcd函数得到输入的有理数的分子和分母的最大公约数(关于av_gcd函数的用法可以参考:《FFmpeg源码:av_gcd函数分析》):

int64_t gcd = av_gcd(FFABS(num), FFABS(den));

FFABS是宏,定义在libavutil/common.h中,作用是得到绝对值:

/*** Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they* are not representable as absolute values of their type. This is the same* as with *abs()* @see FFNABS()*/
#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))

如果最大公约数大于0,让输出的有理数中的分子和分母都除以最大公约数(约分),从而让分数化简。分数的化简的其中一种方法是:化成分数乘法,求出比值,再把比值写成比号链接的形式。即可把一个分数化成和它相等,但分子和分母都比较小的分数,叫做约分,约分时根据分数的基本性质一次性约分(用最大公因数分别去除分子、分母):

if (gcd) {num = FFABS(num) / gcd;den = FFABS(den) / gcd;}

限制化简后的有理数中的分子和分母的绝对值都不能超过形参max的值:

    if (num <= max && den <= max) {a1 = (AVRational) { num, den };den = 0;}

让输出型参数dst_num和dst_den分别得到化简后的有理数的分子和分母:

*dst_num = sign ? -a1.num : a1.num;*dst_den = a1.den;return den == 0;

四、av_reduce函数的使用例子

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

#include <stdio.h>
#include <stdint.h>
#include <limits.h>
#include <features.h>#ifdef __GNUC__
#    define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
#    define AV_GCC_VERSION_AT_MOST(x,y)  (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
#else
#    define AV_GCC_VERSION_AT_LEAST(x,y) 0
#    define AV_GCC_VERSION_AT_MOST(x,y)  0
#endif#ifndef av_always_inline
#if AV_GCC_VERSION_AT_LEAST(3,1)
#    define av_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#    define av_always_inline __forceinline
#else
#    define av_always_inline inline
#endif
#endif#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__)
#    define av_const __attribute__((const))
#else
#    define av_const
#endif#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
#define av_assert2(cond) ((void)0)#ifdef __USE_ISOC99
__extension__ extern long long int llabs (long long int __x)__THROW __attribute__ ((__const__)) __wur;
#endif#ifndef ff_ctzll
#define ff_ctzll ff_ctzll_ctypedef struct AVRational{int num; ///< Numeratorint den; ///< Denominator
} AVRational;/* We use the De-Bruijn method outlined in:* http://supertech.csail.mit.edu/papers/debruijn.pdf. */
static av_always_inline av_const int ff_ctzll_c(long long v)
{static const uint8_t debruijn_ctz64[64] = {0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12};return debruijn_ctz64[(uint64_t)((v & -v) * 0x022FDD63CC95386DU) >> 58];
}
#endifint64_t av_gcd(int64_t a, int64_t b) {int za, zb, k;int64_t u, v;if (a == 0)return b;if (b == 0)return a;za = ff_ctzll(a);zb = ff_ctzll(b);k  = FFMIN(za, zb);u = llabs(a >> za);v = llabs(b >> zb);while (u != v) {if (u > v)FFSWAP(int64_t, v, u);v -= u;v >>= ff_ctzll(v);}return (uint64_t)u << k;
}/*** Reduce a fraction.** This is useful for framerate calculations.** @param[out] dst_num Destination numerator* @param[out] dst_den Destination denominator* @param[in]      num Source numerator* @param[in]      den Source denominator* @param[in]      max Maximum allowed values for `dst_num` & `dst_den`* @return 1 if the operation is exact, 0 otherwise*/
int av_reduce(int *dst_num, int *dst_den,int64_t num, int64_t den, int64_t max)
{AVRational a0 = { 0, 1 }, a1 = { 1, 0 };int sign = (num < 0) ^ (den < 0);int64_t gcd = av_gcd(FFABS(num), FFABS(den));if (gcd) {num = FFABS(num) / gcd;den = FFABS(den) / gcd;}if (num <= max && den <= max) {a1 = (AVRational) { num, den };den = 0;}while (den) {uint64_t x        = num / den;int64_t next_den  = num - den * x;int64_t a2n       = x * a1.num + a0.num;int64_t a2d       = x * a1.den + a0.den;if (a2n > max || a2d > max) {if (a1.num) x =          (max - a0.num) / a1.num;if (a1.den) x = FFMIN(x, (max - a0.den) / a1.den);if (den * (2 * x * a1.den + a0.den) > num * a1.den)a1 = (AVRational) { x * a1.num + a0.num, x * a1.den + a0.den };break;}a0  = a1;a1  = (AVRational) { a2n, a2d };num = den;den = next_den;}av_assert2(av_gcd(a1.num, a1.den) <= 1U);av_assert2(a1.num <= max && a1.den <= max);*dst_num = sign ? -a1.num : a1.num;*dst_den = a1.den;return den == 0;
}int main()
{int dst_num1 = 0;int dst_den1 = 0;int ret = av_reduce(&dst_num1, &dst_den1, 4, 6, 5);printf("ret:%d, dst_num1:%d, dst_den1:%d\n", ret, dst_num1, dst_den1);int dst_num2 = 0;int dst_den2 = 0;ret = av_reduce(&dst_num2, &dst_den2, -4, 6, 5);printf("ret:%d, dst_num2:%d, dst_den2:%d\n", ret, dst_num2, dst_den2);int dst_num3 = 0;int dst_den3 = 0;ret = av_reduce(&dst_num3, &dst_den3, -4, 6, 1);printf("ret:%d, dst_num3:%d, dst_den3:%d\n", ret, dst_num3, dst_den3);return 0;
}

输出如下:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词