========
# 音视频同步的全部知识:
1、既然你都开始关注音视频同步了,那么假设你有了基础的背景知识,比如:有计算机知识;编程理论(知道中断、缓存、状态机、模块);会一门编程语言;知道MPEG ISO协议;知道mp4、mkv、ts等封装格式;知道h264、hevc、mpeg2、mpeg4等视频流格式;知道mp3、aac等音频流格式;知道RGB、YUV、YCrCb、WAV等显示和播放格式;知道显示驱动音频驱动;知道屏幕刷新率vsync。不知道这些的话就需要在实操时遇到了就去搜索学习相关知识。
2、在解封装(从mp4等文件分离出视频流和音频流)的时候获取视频帧率(每帧画面显示时间);视频宽高参数;每帧视频的dts、pts,并转换成相对于文件开始播放时的相对时间(单位为ms);音频帧dts;音频采样频率、通道数、采样深度;需要有缓存存储视频帧和音频帧并向下级模块传递缓存,每帧缓存中都要携带该帧的pts时间,如果是用固定缓存,固定缓存里一次性存放多个帧或者连帧都不是完整的,只是直接送到解码模块的话,你需要在每个缓存中携带最早的pts,并在之后能对pts进行推算还原。
2.1、解封装时你可以直接按需求流式的读文件,读需要的长度,需要解析多长就读多长,甚至在文件中前后来回跳读非固定长度的数据,但是这样会降低你系统的负担,没有操作系统文件系统支撑的话会增加IO读取次数,增加读取文件带来的时间消耗,这时候你可能需要解封装时每次预缓存固定长度的文件数据,然后对缓存中的数据进行解析,这也是一个比较复杂的逻辑,需要做各种切换处理。而且一般音视频播放模块缓存的数据帧时长不一样,你可能视频和音频各需要一个读数据帧的缓存,它们在文件中当前读取的地址还前后差了一段距离。
3、音频和视频解码都支持按帧输入和随意长度输入,能按帧输入最好,但是为了节省执行次数和更好利用缓存资源也可以按缓存长度输入,音视频解码时都需要将输入时最前面一帧的pts和解码后每帧的pts对应起来,解码后的数据一般都是定长的,对定长的数据进行显示和播放时也比较方便。
4、音视频流各自解码后的数据帧都送到音视频同步模块,同步模块将音频直接送到播放模块进行播放,但是送去时需要记录下当前播放帧的pts、播放时音频播放模块当前正缓存的数据长度和占用的时间,音频播放获取数据一般是通过回调或者中断的方式获取的,这个时候就需要在回调的时候记录这些信息,通过这些信息算出音频时间轴。(这是视频同步到音频的主流做法,音频同步到视频和音视频都同步到主时间轴的方法这里不介绍)
4.1、视频显示时需要记录下当前帧显示的系统时间点和帧的pts,算出视频时间轴。
4.2、通过音视频同步算法和当前音视频的时间基确定当前视频帧是提前显示、推后显示还是丢帧。一般视频超前音频100ms到落后20ms人耳感觉不出来,在这个范围内视频可以按原有的帧率进行显示,否则就要提前或者推后显示当前帧。
4.3、一般视频解码时输入和输出的结构体中都带有私有参数,这个参数可以让你输入的帧顺序和输出的帧顺序匹配,解码后输出帧的私有参数数据就是解码前帧数据中的内容。基于I帧、P帧、B帧双向预测编解码的方式,视频解码顺序和播放顺序是不一样的,解码时间戳是dts,显示时间戳是dts,视频解码模块的这种机制可以让你不需要自己计算帧时间的匹配,因为视频解码输入和输出都有帧缓存机制,很难计算每帧时间戳pts。一帧解码前的视频帧,可能会解码出好几帧,这种机制还需要你人为处理好这种情况,因为这是解码出来的视频帧都带有同样的私有参数(也就是pts时间戳)。
5、视频显示模块有固定的vsync刷新率,如60Hz,如果视频流帧率和显示模块帧率接近的话还需要在vsync切换的时间点进行显存内容刷新,将时序匹配好。
6、音频播放模块实际的播放速度有可能不是音频流标准的速度,如44100Hz,这时算音频时间轴时要做好匹配。
6.1、进行倍速播放时,为了保证音频不失真,还需要对音频帧数据进行重采样,保证音频中的频率也按倍速调整,这个涉及到算法,这里不介绍。