欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ffmpeg中dts和pts的转换

程序员文章站 2022-07-02 08:28:44
...

FFMPEG的AVRational time_base:

typedef struct AVRational{
    int num; ///< numerator
    int den; ///< denominator
} AVRational;

AVRational这个结构标识一个分数,num为分数,den为分母。

原文链接:
https://blog.csdn.net/zhuweigangzwg/article/details/64919706
参考:
http://blog.chinaunix.net/uid-20554957-id-5836134.html
https://blog.csdn.net/topsluo/article/details/77981732

time_base:
它是用来度量时间的。
如果把1秒分为25等份,你可以理解就是一把尺,那么每一格表示的就是1/25秒。此时的time_base={1,25}
如果你是把1秒分成90000份,每一个刻度就是1/90000秒,此时的time_base={1,90000}。

所谓时间基表示的就是每个刻度是多少秒
pts的值就是占多少个时间刻度(占多少个格子)。它的单位不是秒,而是时间刻度。只有pts加上time_base两者同时在一起,才能表达出时间是多少。
好比我只告诉你,某物体的长度占某一把尺上的20个刻度。但是我不告诉你,这把尺总共是多少厘米的,你就没办法计算每个刻度是多少厘米,你也就无法知道物体的长度。
pts=20个刻度
time_base={1,10} 每一个刻度是1/10厘米
所以物体的长度=ptstime_base=201/10 厘米

duration和pts单位一样,duration表示当前帧的持续时间占多少格。或者理解是两帧的间隔时间是占多少格。一定要理解单位。
pts:格子数

av_q2d(st->time_base):/

计算视频长度:

time() = st->duration * av_q2d(st->time_base)

av_q2d(time_base)=每个刻度是多长时间
此时你应该不难理解 pts*av_q2d(time_base)才是帧的显示时间戳。

案例一:
我们从rtsp拉流后转rtmp推流,要进行dts和pts的转换,这种转换很简单,因为输入的dts没有被破坏,只需要简单转换一下就可以了。

frame.pts = av_rescale_q_rnd(
        frame.pts, m_Video_Input_time_base, outStream->time_base,
        static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    frame.dts = av_rescale_q_rnd(
        frame.dts, m_Video_Input_time_base, outStream->time_base,
        static_cast<AVRounding>(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
    frame.duration = av_rescale_q(frame.duration, m_Video_Input_time_base,
                                  outStream->time_base);

其中m_Video_Input_time_base为inputstream 的AVstream中的timebase,frame.dts为发送出去rtmp包的timebase。

案例二:
假如我们将输入rtsp流解码后压缩一下尺寸,再推送到rtmp上去。
因为我们编码的时候设置的参数:

  pAVCodecCtx->time_base.num = 1;
  pAVCodecCtx->time_base.den = 25;
//这里保存一下编码后输出的dts
int64 temp_dts = frame.dts;
if (m_firstImg) {
    //第一帧必须为关键帧
     if (!(frame.flags & AV_PKT_FLAG_KEY))
        return false;
      m_firstImg = false;
      m_curTamp = 0;
      frame.pts = m_curTamp;
      frame.dts = m_curTamp;
      //这里很重要了,输出的流1s有den个格子,除以25就是目前一帧所持续时间(两帧间隔)
      frame.duration = outStream->time_base.den / 25;
      m_lastDuration = frame.duration;
    } else {
    //如果编码出来的帧dts有误,比如duration大于真确的duration,或者dts比上一张的小,那么我们就自己
    //手动进行赋值
      if ((abs(frame.dts - m_lastDts) > (outStream->time_base.den / 25)) ||
          (frame.dts <= m_lastDts)) {
        m_curTamp += m_lastDuration;
        frame.pts = m_curTamp;
        frame.dts = m_curTamp;
        frame.duration = m_lastDuration;
      } else {
      //将编码出来帧的duration进行不同timebase的转换
        m_lastDuration =
            av_rescale_q((frame.dts - m_lastDts), {125}, outStream->time_base);
        //dts就是将duration逐渐累加,比如第10帧就是10*duration
        m_curTamp += m_lastDuration;
        frame.pts = m_curTamp;
        frame.dts = m_curTamp;
        frame.duration = m_lastDuration;
      }
    }
    m_lastDts = temp_dts;
    m_duration += m_lastDuration;

案例三:
这是雷神写的一段:
入口

PTS/DTS问题
没有封装格式的裸流(例如H.264裸流)是不包含PTS、DTS这些参数的。在发送这种数据的时候,需要自己计算并写入AVPacket的PTS,DTS,duration等参数。这里还没有深入研究,简单写了一点代码,如下所示。

//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS
if(pkt.pts==AV_NOPTS_VALUE){
	//Write PTS
	AVRational time_base1 = ifmt_ctx->streams[videoindex]->time_base;
	//Duration between 2 frames (us)
	//比如1s有25帧,那么就是AV_TIME_BASE/25,av_q2d来计算输入帧率
	int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
	//Parameters,pts就是持续了多少格,
	//frame_index*calc_duration,这个是基于AV_TIME_BASE来确定的,输出的时候我们还要转一次,时间基准
	//转到输入流的基准上去,做完这一步还要接着将timebase转到输出流的timebase上去
	pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
	//因为没有b帧,所以dts=pts
	pkt.dts=pkt.pts;
	//
	pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
}


        /* copy packet */
		//转换PTS/DTS(Convert PTS/DTS)
		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;



//ffmpeg代码
/**
 * 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;
}

ypedef struct AVStream {
    int index;    /**< stream index in AVFormatContext */
    AVCodecContext *codec;
     /**
     * This is the fundamental unit of time (in seconds) in terms
     * of which frame timestamps are represented.
     *
     * decoding: set by libavformat
     * encoding: May be set by the caller before avformat_write_header() to
     *           provide a hint to the muxer about the desired timebase. In
     *           avformat_write_header(), the muxer will overwrite this field
     *           with the timebase that will actually be used for the timestamps
     *           written into the file (which may or may not be related to the
     *           user-provided one, depending on the format).
     */
    AVRational time_base;

   /**
     * Real base framerate of the stream.
     * This is the lowest framerate with which all timestamps can be
     * represented accurately (it is the least common multiple of all
     * framerates in the stream). Note, this value is just a guess!
     * For example, if the time base is 1/90000 and all frames have either
     * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
     */
    AVRational r_frame_rate;
}

#define AV_TIME_BASE 1000000

相关标签: 音视频资料