先讲一个概念:
pts :音视频显示时间。
dts:音视频解码时间。
time_base: 时间刻度单位,时间基。例如视频播放30fps,表示1秒钟播放30张图片,视频的时间基time_base等于{1,30};如果音频的采样率是44100Hz,音频的时间基是{1,44100}
timebase,在ffmpeg数据结构表示
typedef struct AVRational{
int num; ///< Numerator
int den; ///< Denominator
} AVRational;
duration:视频的总时长。time(秒) = st->duration * av_q2d(st->time_base);
在ffmpeg中。av_q2d(time_base)=每个刻度是多少秒。pts*av_q2d(time_base)才是帧的显示时间。
因为数据状态不同(不同的封装格式,压缩前、压缩后的数据以及封装前和封装后的数据不一样),对于封装格式来说:flv 封装格式的 time_base 为{1,1000},ts 封装格式的 time_base 为{1,90000}。时间基不一样,所以我们必须转换,在1/30时间刻度下占10格,在1/90000下是占多少格。这就是pts时间戳的转换。
ffmpeg,给到av_rescale_q(int64_t a, AVRational bq, AVRational cq)函数
这个函数的作用是计算a*bq / cq(pts的转换)来把时间戳从一个时间基调整到另外一个时间基。在进行时间基转换的时候,应该首先用这个函数,因为它可以避免溢出的情况发生。
函数表示在bq下的占a个格子,在cq下是多少。
音质为例44100Hz的采样率,假设一帧音频包含1024个采样数据,那么1秒钟的音频大约有43帧。在编码阶段无论是视频还是音频我们都需要提供一个基础的pts作为参考。代表视频的vpts每次自增1即可,而代表音频的apts需要每次自增1024。
编码器到容器需要做时间转换,视频H264时间基{1,30},转换成flv{1,1000},音频的时间基{1,44100}转换成flv{1,1000},使用av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb)函数进行转换,其实内部也是根据av_rescale_q实现。
视频和音频的同步实际上是一个动态的过程,同步是暂时的,不同步则是常态。以选择的播放速度量为标准,快的等待慢的,慢的则加快速度,是一个你等我赶的过程。
播放速度标准量的的选择一般来说有以下三种:
推流工作,将视频的pts和音频的pts设置正确。pts一般累加1,音频是加1024 没做任何处理。
不过累加的方式有2种情况可能会有问题,1)输入帧率不稳定 2)视频处理时间过久,到时客户端接收的PTS超过expireTime,被丢弃。
第一个问题的话,其实和驱动和硬件有关系。
驱动要把读取到的帧率正确设置给编码器
所以此时要用av_compare_ts去做比较
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b)
假如av_compare_ts(vpts, pVideoCodecCtx->time_base, apts, pAudioCodecCtx->time_base);
比较音视频pts,大于0表示音视频时间快于视频,音频需要连续编码。小于0表示,视频时间快于音频,应该至少编码一帧视频。