概述
本文将以具体视频播放器开发过程中遇到的具体问题,来系统地阐释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);