ffmpeg 时间相关--时间基,timebase,pts,dts,duration

在编码时 video 的情况如下:

1. 在 yuv 数据 阶段,和时间相关参数如下:

yuv数据我们在设置的时候要 设置参数,其中和时间相关的是 yuvfps 和 timebase,yuvfps一般是每秒25帧,yuvfps=25;timebase 一般设置为 1000,000 这样 我们就能计算出来再yuv数据阶段一张图片花费时间为:  1/25 * 1000000 = 40,000 

整理:也就是说:我们在yuv阶段,yuv的timebase 是1000000,yuv的pts 的计算是每次读取一张图片,就给pts+= 1.0 / yuvfps * videotimebase

这两个值都是开发者设定的。

2. 当yuv数据在变成AVFrame时的过程中,时间相关

在从yuv 变成 avframe的过程中,一定要弄一个AVCodecContext,这时候要设置AVCodecContext 的相关参数,除了video的三要素外,还需要设置 AVCodecContext.timebase 为 1000000。

在将yuv数据变成AVFrame的代码中,我们需要重新计算并设置 AVFrame的pts。如何设置呢?

yuv的pts * yuv的timebase = 这张图片显示的时间 = avframe阶段的 pts  * avframe阶段的 timebase

avframe阶段的 pts = yuv的pts * yuv的timebase/avframe阶段的 timebase

整理:

avframe阶段的 timebase就是avcodeContext 中的timebase,对于video 来说:avcodeContext 中的timebase也是开发者手动设自动的。

在计算出来avframe的pts 后,记得设置avframe的pts的值。

3.当从avframe变成avpacket的过程中。时间相关

在前面,我们计算了avframe的pts,并设置了avframe的pts,也知道avframe的timebase 就是avcodecContext的timebase。

那么avpacket 的pts 也要重新计算。

AVFrame pts * avFrame timebase = 这张图片的显示时间 = avpacket pts * avpacket timebase

也就是:

avpacket pts = AVFrame pts * avFrame timebase / avpacket timebase

AVFrame pts 和 avFrame timebase 的值我们在前面已经知道了,

关键问题是 avpacket timebase 是怎么来的呢?

实际上是,avpacket timebase 使用的是 AVStream中的timebase,那么AVStream 的timebase又是怎么来的呢?当我们使用 

avformat_write_header(this->_avformatContext,nullptr);

发送头部的后,AVStream的timebase就有值了,不同的格式,AVStream的timebase不同,对于video来说,很多都是1,90000.

值的注意的是,我们在将avframe变成avpacket的时候,这时候还没有调用 avformat_write_header这个方法,因此这时候avpacket 中的pts,dts ,duration是和avframe的pts,dts,duration一样的值。

4. 最终将avpacket 变成 MP4或者flv,时间相关

上述第三步,更加准确的说:在avframe 变成 avpacket后,avpacket的pts和 avframe的pts 是一样的。

一般情况下,我们在调用了 是当 调用了 avformat_write_header 方法后,才会将 avpacket->pts重新计算,使用的 avpacket的pts会是 avstream中的pts

而且在这个时候 会将 avpacket 的dts,duration,pts都计算完成。

    dst_timebase = this->_video_avstream->time_base;
    avpacket->pts = av_rescale_q(avpacket->pts, src_timebase,dst_timebase);
    avpacket->dts = av_rescale_q(avpacket->dts, src_timebase,dst_timebase);
    avpacket->duration = av_rescale_q(avpacket->duration, src_timebase,dst_timebase);

在编码时 audio 的情况如下:

1. 在 pcm 数据 阶段,和时间相关参数如下:

我们以 44100 的pcm 转换成 aac 的例子说明:

我们在还是pcm数据的时候,会计算出来1024个样本帧的花费的时间,具体的计算公式为 1024/44100 * 1000,000。这里1000000 是转化为微秒的时间基。

2  avframe阶段的计算,和时间相关参数如下:


/// 当我们将读取的数据要存储到avframe中的时候,就需要重新计算avframe的pts,使用avframe对应的时间基
/// 首先要明确的是的 对于avframe 对应的时间基用的是avcodec中的timebase,那么avcodec中的timebase是怎么知道的呢?观察源码会发现,音频编码器上下文的timebase 是在 avcodec_open2方法中 设置的,且设置的值的为 avcodecContext中的 sample_rate 的倒数,也就是1/44100
/// 注意,avcodecContext中的 sample_rate实际上是需要user 手动的设定的,因此你就明白了为什么在 编码的时候一定要设置 编码器上下文的 sample_rate了
/// 那么现在我们有了三个值了,一个是 pcm的pts,一个是pcm的时间基,一个是avframe的时间基,求avframe的pts?
///  pcm的pts * pcm 的时间基 = avframe 的pts * avframe的时间基(也就是avcodecContext的timebase)
/// 结论为 :avframe 的pts  = pcm的pts * pcm 的时间基 / avframe的时间基(avcodecContext的timebase)
/// 为了防止内存溢出等问题,使用ffmpeg给我们提供的方法 :av_rescale_q(pts, AVRational{1, (int)time_base}, this->_avcodecContext->time_base)
/// 可以观察 :av_rescale_q方法的本质,就是第一个参数乘以第二个参数,最后除以第三个参数

3 avpacket阶段的计算


/// 到这里,我们最终生成的avpacket的 pts,dts,duration的值又是多少呢?
/// 实际上,在我们这个阶段,avpacket的 pts,dts,duration的值会直接 copy avframe中的pts,dts,duration(都是通过avcodec_receive_packet(this->_avcodecContext, avpacket) 方法传递的)
/// 这明显是不合理的。我们可以回顾一下 avframe 和avpacket 的本质,avframe装的是未压缩的pcm数据,avpacket装的是压缩过的pcm数据(也就是类似aac这样的数据)
/// 那么依然存在着  avframe的pts(当前已经存储在avpacket的pts) 转化成 真正的avpacket的pts的问题,那么avframe的时间基我们是知道的--是avcodec中的timebase,那么 这个真正的avpacket的时间基是什么呢?是avstream中的timebase
/// 那么avstream中的timebase怎么知道呢?实际上不同的编码器,它的avstream中的timebase是不同的,ffmpeg在你调用 avformat_write_header(fmt_ctx_, NULL);函数的时候就确定了您的timebase 是多少了,这也很容易想到,对于不同的编码器,它的头部信息一定就含有了 timebase这样重要的信息
/// avframe 的pts(当前旧的avpacket的pts) * avframe的时间基(avcodec的时间基) = 真正的avpacket的pts * 真正的avpacket的时间基(也就是avstream的时间基)
/// 结论为 :真正的avpacket的pts  = avframe 的pts(当前旧的avpacket的pts) * avframe的时间基(avcodec的时间基) / 真正的avpacket的时间基(也就是avstream的时间基)
/// 对应代码在 muxer.cpp的sendPacket中。

在解码时 video 的情况如下:

在解码时 audio 的情况如下:






 

/// 关于时间问题,看了网上的资料,大致结论如下,

/// 不同结构体的 time_base
///1、AVStream的time_base的单位是秒。每种格式的time_base的值不一样,根据采样来计算,比如mpeg的pts、dts都是以90kHz来采样的,所以采样间隔就是1/900000秒。
///2、AVCodecContext的time_base单位同样为秒,不过精度没有AVStream->time_base高,大小为1/framerate。
///3、AVPacket下的pts和dts以AVStream->time_base为单位(数值比较大),时间间隔就是AVStream->time_base。
///4、AVFrame里面的pkt_pts和pkt_dts是拷贝自AVPacket,同样以AVStream->time_base为单位;而pts是为输出(显示)准备的,以AVCodecContex->time_base为单位。
///5、输入流InputStream下的pts和dts以AV_TIME_BASE为单位(微秒),至于为什么要转化为微秒,可能是为了避免使用浮点数。
///6、输出流OutputStream涉及音视频同步,结构和InputStream不同,暂时只作记录,不分析
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值