ffmpeg音视频同步

先讲一个概念:

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表示,视频时间快于音频,应该至少编码一帧视频。

你可能感兴趣的:(音视频,ffmpeg,音视频)