• FFmpeg开发——深入理解pts,dts和timebase


    概述

    本文将以具体视频播放器开发过程中遇到的具体问题,来系统地阐释pts,dts和timebase的概念。

    1.时间基

    FFmpeg开发中,经常会遇到结构体中有time_base这个成员,通过头文件查看他的类型是AVRational

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

    那么AVRational到底表示了什么呢?

    AVRational这个结构标识一个分数,num为分子,den为分母。实际上time_base的意思就是时间的刻度。

    如果把1秒分为25等份,你可以理解就是一把尺,那么每一格表示的就是1/25秒。此时的time_base={1,25}。如果你是把1秒分成90000份,每一个刻度就是1/90000秒,此时的time_base={1,90000}。所谓时间基表示的就是每个刻度是多少秒。
    那么,在刻度为1/25的体系下的time=5,转换成在刻度为1/90000体系下的时间time为(5x1/25)/(1/90000) = 3600*5=18000

    正是由于不同的封装格式,timebase是不一样的。另外,整个转码过程,不同的数据状态对应的时间基也不一致。所以在实际开发过程中,存在着大量时间基的转换。

    2.PTS和DTS

    PTS:Presentation Time Stamp。PTS 主要用于度量解码后的视频帧什么时候被显示出来。
    DTS:Decode Time Stamp。DTS 主要是标识读入内存中的Bit流在什么时候开始送入解码器中进行解码。

    虽然 DTS、PTS 是用于指导播放端的行为,但它们是在编码的时候由编码器生成的。当视频流中没有 B 帧时,通常 DTS 和 PTS 的顺序是一致的。但如果有 B 帧时,解码顺序和播放顺序不一致了。

    来看一个具体的例子,利用雷神做的videoeye视频码流分析软件,来对视频文件进行分析,这个文件是mp4格式的,可以看到视频码流PTS在递增,这就是我们看到画面的顺序,但是码流顺序并不是递增的,这里的码流顺序可理解为解码的顺序,也就是DTS表示的意思,先解I帧,再解P帧,依次解中间的B帧
    在这里插入图片描述

    而音频的解码顺序就是我们依次听到的顺序,PTS和DTS相等

    在这里插入图片描述
    怎么理解PTS数值表达的含义呢,如果有某一帧,假设它是第10秒开始显示。那么它的pts是多少呢。是10?还是10s?还是两者都不是。

    这就引出了pts和dts的值到底代表了什么含义这个问题

    pts和dts的值指的是占多少个时间刻度(占多少个格子)。它的单位不是秒,而是时间刻度。只有pts与time_base两者结合在一起,才能表达出具体的时间是多少。好比我只告诉你,某个物体的长度占某一把尺上的20个刻度。但是我不告诉你,每个刻度是多少厘米,你仍然无法知道物体的长度。pts 就是这样的东西,pts(占了多少个时间刻度) ,time_base(每个时间刻度是多少秒) ,而帧的显示时间戳 = pts(占了多少个时间刻度) * time_base(每个时间刻度是多少秒)。

    2.一些开发过程中时间基转换的场景

    1.计算视频总时长

    AVFormatContext *ifmt_ctx = NULL;
    avformat_open_input(&ifmt_ctx, filename, NULL, NULL);
    int totalMs;//视频总毫秒数
    totalMs = ifmt_ctx->duration / (AV_TIME_BASE / 1000);

    2.根据PTS求出一帧在视频中对应的秒数位置

    double sec = enc_pkt.pts * av_q2d(ofmt_ctx->streams[stream_index]->time_base);

    3.ffmpeg内部的时间戳与标准的时间转换方法

    //timestamp为ffmpeg内部时间戳,time为正常时间戳,单位为秒
    timestamp = AV_TIME_BASE * time
    time = AV_TIME_BASE_Q * timestamp

    AV_TIME_BASE这个宏为1000000,由此我们可以发现ffmpeg内部时间戳是以微秒(μs)为单位的

    4.当需要把视频Seek到N秒的时候

    //pos单位毫秒
    double pos;
    seekPos = ifmtctx->streams[videoStream]->duration * pos;
    av_seek_frame(ifmtctx, videoStream, seekPos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
     
  • 相关阅读:
    扩展一些std::string未提供的常用方法
    Qt子线程中显示窗口部件的一个方法
    Qt子线程中通过QMetaObject::invokeMethod刷新UI控件
    “我的一剂良药”之开源指北
    源码解析之 Mybatis 对 Integer 参数做了什么手脚?
    必知必会面试题之 Spring 基础
    从一部电影史上的趣事了解 Spring 中的循环依赖问题
    Mysql、Oracle、SQL-Server 查询字段值长度
    Cross-Origin Read Blocking (CORB) blocked cross-origin response 问题
    MacOS11.0-brew 卡在Updating Homebrew
  • 原文地址:https://www.cnblogs.com/lidabo/p/16308405.html
Copyright © 2020-2023  润新知