ffmpeg之时间戳 - PTS ,DTS

2017-08-21  本文已影响0人  easyhao

TOC

视频的显示和存放原理

对于一个电影,帧是这样来显示的:I B B P。现在我们需要在显示B帧之前知道P帧中的信息。因此,帧可能会按照这样的方式来存储:IPBB。这就是为什么我们会有一个解码时间戳和一个显示时间戳的原因。解码时间戳告诉我们什么时候需要解码,显示时间戳告诉我们什么时候需要显示。所以,在这种情况下,我们的流可以是这样的:

PTS: 1 4 2 3
DTS: 1 2 3 4
Stream: I P B B

通常PTS和DTS只有在流中有B帧的时候会不同。

DTS和PTS

音频和视频流都有一些关于以多快速度和什么时间来播放它们的信息在里面。音频流有采样,视频流有每秒的帧率。然而,如果我们只是简单的通过数帧和乘以帧率的方式来同步视频,那么就很有可能会失去同步。于是作为一种补充,在流中的包有种叫做DTS(解码时间戳)和PTS(显示时间戳)的机制。为了这两个参数,你需要了解电影存放的方式。像MPEG等格式,使用被叫做B帧(B表示双向bidrectional)的方式。另外两种帧被叫做I帧和P帧(I表示关键帧,P表示预测帧)。I帧包含了某个特定的完整图像。P帧依赖于前面的I帧和P帧并且使用比较或者差分的方式来编码。B帧与P帧有点类似,但是它是依赖于前面和后面的帧的信息的。这也就解释了为什么我们可能在调用avcodec_decode_video以后会得不到一帧图像。

ffmpeg中的时间单位

AV_TIME_BASE

ffmpeg中的内部计时单位(时间基),ffmepg中的所有时间都是于它为一个单位,比如AVStream中的duration即以为着这个流的长度为duration个AV_TIME_BASE。AV_TIME_BASE定义为:

#define AV_TIME_BASE 1000000

关于时间戳的几个函数

1. av_rescale_rnd

函数声明

int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)

直接看代码, 它的作用是计算 "a * b / c" 的值并分五种方式来取整.
但是在FFmpeg中,则是将以 "时钟基c" 表示的 数值a 转换成以 "时钟基b" 来表示。

看AVRounding结构体,就是这五种方式

enum AVRounding {
    AV_ROUND_ZERO     = 0, ///< Round toward zero.        靠近0
    AV_ROUND_INF      = 1, ///< Round away from zero.     远离0
    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. 四舍五入,小于0.5取值趋向0,大于0.5取值趋远于0
    /**
     * 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,
};

函数实现

int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
{
    int64_t r = 0;
    av_assert2(c > 0);
    av_assert2(b >=0);
    av_assert2((unsigned)(rnd&~AV_ROUND_PASS_MINMAX)<=5 && (rnd&~AV_ROUND_PASS_MINMAX)!=4);

    if (c <= 0 || b < 0 || !((unsigned)(rnd&~AV_ROUND_PASS_MINMAX)<=5 && (rnd&~AV_ROUND_PASS_MINMAX)!=4))
        return INT64_MIN;

    if (rnd & AV_ROUND_PASS_MINMAX) {
        if (a == INT64_MIN || a == INT64_MAX)
            return a;
        rnd -= AV_ROUND_PASS_MINMAX;
    }

    if (a < 0)
        return -(uint64_t)av_rescale_rnd(-FFMAX(a, -INT64_MAX), b, c, rnd ^ ((rnd >> 1) & 1));

    if (rnd == AV_ROUND_NEAR_INF)
        r = c / 2;
    else if (rnd & 1)
        r = c - 1;

    if (b <= INT_MAX && c <= INT_MAX) {
        if (a <= INT_MAX)
            return (a * b + r) / c;
        else {
            int64_t ad = a / c;
            int64_t a2 = (a % c * b + r) / c;
            if (ad >= INT32_MAX && b && ad > (INT64_MAX - a2) / b)
                return INT64_MIN;
            return ad * b + a2;
        }
    } else {
#if 1
        uint64_t a0  = a & 0xFFFFFFFF;
        uint64_t a1  = a >> 32;
        uint64_t b0  = b & 0xFFFFFFFF;
        uint64_t b1  = b >> 32;
        uint64_t t1  = a0 * b1 + a1 * b0;
        uint64_t t1a = t1 << 32;
        int i;

        a0  = a0 * b0 + t1a;
        a1  = a1 * b1 + (t1 >> 32) + (a0 < t1a);
        a0 += r;
        a1 += a0 < r;

        for (i = 63; i >= 0; i--) {
            a1 += a1 + ((a0 >> i) & 1);
            t1 += t1;
            if (c <= a1) {
                a1 -= c;
                t1++;
            }
        }
        if (t1 > INT64_MAX)
            return INT64_MIN;
        return t1;
#else
        /* reference code doing (a*b + r) / c, requires libavutil/integer.h */
        AVInteger ai;
        ai = av_mul_i(av_int2i(a), av_int2i(b));
        ai = av_add_i(ai, av_int2i(r));

        return av_i2int(av_div_i(ai, av_int2i(c)));
#endif
    }
}

2.av_rescale_q_rnd

函数定义

int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,
                         enum AVRounding rnd)
{
    int64_t b = bq.num * (int64_t)cq.den;
    int64_t c = cq.num * (int64_t)bq.den;
    return av_rescale_rnd(a, b, c, rnd);
}

将以时钟基为c 的时间戳a 转换成以b为时钟基并且以rnd 这种方式进行运算的值

3.av_rescale_q

函数定义

int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
{
    return av_rescale_q_rnd(a, bq, cq, AV_ROUND_NEAR_INF);
}

例子

将以"1MHz时钟基" 表示的 "PTS/DTS值a" 转换成以 "90kHz时钟基" 表示。

//调用转换
int64_t  av_rescale_q(pkt->pts=-10949117256, src_tb={num=1, den=1000000}, dst_tb{num=1, den=90000))
{
  return av_rescale_q_rnd(a, bq, cq, AV_ROUND_NEAR_INF);
}
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,
                         enum AVRounding rnd)
{
    int64_t b = bq.num * (int64_t)cq.den;// = 1 * 90000   = 90000;  
    int64_t c = cq.num * (int64_t)bq.den; // = 1 * 1000000 = 1000000  
    return av_rescale_rnd(a, b, c, 5);
}

int64_t av_rescale_rnd(a=10949117256, b=90000, c=1000000, rnd=5)  
{  
  if (rnd==5)   
    r = c / 2;                          // r =500000;  
    
  if (b<=INT_MAX && c<=INT_MAX)  
  {  
    if (a<=INT_MAX)  
      return (a * b + r)/c;  
    else  
      return a/c*b + (a%c*b + r)/c;    // =  10949117256 / 1000000 * 90000 +   
                                       //   (10949117256 % 1000000 * 90000 + 500000) / 1000000  
                                       // =  985420553  
  }  
  else  
  {  
    ...  
  }    
}  
上一篇下一篇

猜你喜欢

热点阅读