• 新手学习FFmpeg


    通过简单的计算来,线上I帧在视频中出现的时间点。 完整代码请参考 https://andy-zhangtao.github.io/ffmpeg-examples/

    名词解释

    首先需要明确以下名词概念:

    • I/P/B 帧(具体差异请参看 https://www.jianshu.com/p/18af03556431 )
      I帧: 内部编码帧(关键帧)
      P帧: 前向预测帧(根据I帧计算差值)
      B帧: 双向预测帧(根据I帧和P帧计算差值)
    • PTS: 帧显示的时间刻度(在哪个时间点显示此帧)
    • DTS: 帧解码的时间刻度(在哪个时间点解码此帧)
    • Timestamp: 帧在视频内部的时间戳
    • Time_base: 视频表示时间的"刻度"

    处理流程

    视频内没有绝对时间,只有相对时间(相对视频起始位置)。例如在播放器中看到的时间进度条"00:00:05"表示的是当前看到的帧是在相对起始时间点(00:00:00)解码并渲染的。

    而"00:00:05"只是为了让用户方便理解而展现出来的,在视频内部则是使用时间戳来保存的,"00:00:05"可能相对的时间戳则是"5000000µs"(不考虑四舍五入)。

    那么时间戳又是怎么计算出来的呢?此时就需要通过PTS和Time_base来配合计算。

    首先来看Time_base。 Time_base好比一把尺子,上面标满了刻度,例如(1,60)表示时间刻度就是1/60,每个时间单位就是1/60秒。 如果是(1,1000)就表示每个时间单位是1微秒。

    上面说到pts是显示的时间刻度, 也就是占用了多少时间刻度。 换算成大白话就是pts占用了多少个刻度,而time_base表示每个刻度是多长。

    然而这有什么用呢?Time_base最重要的作用是用来统一”时间节奏"的。 例如视频A编码时采用1/1000的time_base,则某个帧的pts保存为465000。 当对视频A进行解码时,换成了1/9000的time_base,此时时间刻度不一致了,就需要通过pts*encode_time_base来换算成解码时的timestamp,这样才能保证正确解码。

    编码实现

    上面是理论介绍,下面来看如何通过代码来计算timestamp和换算成time.

    这次只需要显示每帧的pts,time_base,time因此不需要初始化output, 只要初始化input即可。

    初始化输入源

    按照前几篇介绍的初始化思路,只需要按照打开文件->判断视频流->初始化解码器这样的步骤就可以了。

        +------------------------+              +-------------------------+
        |  avformat_open_input   | ------------>|avformat_find_stream_info|
        +------------------------+              +-------------------------+
                                                          |
                                                          |
                                                          |
                                                         |/
       +-----------------------------+           +-------------------------+
       |avcodec_parameters_to_context| <---------|   avcodec_find_decoder  |
       +-----------------------------+           +-------------------------+
    

    avcodec_parameters_to_context尤其需要关注,这个函数会根据输入源的编码信息来初始化用户指定的编码上下文。如果编码信息不匹配或者设置错误时,会出现莫名的解码错误。一般调用这个函数后,大多数的解码错误都能消失。

    计算Time

    time_base是一个struct

    typedef struct AVRational{
        int num; ///< Numerator
        int den; ///< Denominator
    } AVRational;
    

    num 表示的是分子,den表示分母。 对于time_base来说num就是1,den表示每一秒等分了多少份。 上面说过通过pts*time_base就可以得出时间戳,所以需要计算出每个时间刻度具体代表多少,所以通过av_q2d得出每个刻度具体值。

    在循环读入解码后的帧数据之后,可以直接通过iframe->pts来读取当前帧的pts值,然后再乘以刻度值就可以得出当前时间戳iframe->pts * av_q2d(_time_base)

    伪代码如下:

    while av_read_frame {
        avcodec_send_packet
        ...
        while avcodec_receive_frame {
            ...
            iframe->pts * av_q2d(_time_base)
            ...
        }
    }
    
  • 相关阅读:
    华为lab-rs-v1-2.11_OSPF与ISIS互通
    jdk源码分析红黑树——插入篇
    jdk源码分析PriorityQueue
    jdk源码分析ArrayDeque
    jdk链表笔记
    jdk顺序表笔记
    SpringMVC类型转换器、属性编辑器
    SpringMVC基本使用
    spring整合hibernate
    spring aop注解配置
  • 原文地址:https://www.cnblogs.com/vikings-blog/p/11532546.html
Copyright © 2020-2023  润新知