• 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都计算完成。

    1. dst_timebase = this->_video_avstream->time_base;
    2. avpacket->pts = av_rescale_q(avpacket->pts, src_timebase,dst_timebase);
    3. avpacket->dts = av_rescale_q(avpacket->dts, src_timebase,dst_timebase);
    4. 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不同,暂时只作记录,不分析
  • 相关阅读:
    XShell与XFtp的安装及简单使用
    [2024年]-flink面试真题(三)
    你真的懂synchronized锁?
    STM32面试相关问题
    一个Vue3数字框区间指令及其衍生的一个知识点
    Linux常用命令:find、grep、vim、cat、less、more
    1、编辑利器vim
    论文阅读:SuMa++: Efficient LiDAR-based Semantic SLAM
    sqli-labs关卡20(基于http头部报错盲注)通关思路
    shel脚本-更新hosts
  • 原文地址:https://blog.csdn.net/hunandede/article/details/140400836