前言:
大家好,由于最近工作上碰到一些关于音视频不同步的问题,比如音频跟不上视频的播放速度;基于此,今天给大家分享一篇关于音视频同步的问题,本系列文章会分为几篇来分享,先从基础的音视频同步理论开始,然后再进行基于ffplay里面的源码实战来加深音视频同步的理解!
一、音视频同步基础:
首先我们先来了解一下什么是音视频同步,相信之前看过前面几期关于ffplay源码解析的文章的朋友应该知道,在一个播放器进行解码拿到数据这个环节里面,音频和视频各自有自己的线程进行处理各自对应的数据!
那么在这个环节就可能会出现问题,比如说,音频线程和视频线程可能不会同时解出音视频和视频帧,那么这就会造成后面的播放出现不同问题!也就会出现音视频不同的问题,直白的说,音视频同步就好比我们平时用一个播放器去播放一个视频文件(包括音频和视频!),会出现视频画面和视频画面人物说话的声音对应不上,这就会造成非常差的体验感。
所以为了今后更好的解决实际开发问题,我们在此之前需要掌握一些基础理论知识,下面我们来看一下什么是PTS和DTS、timebase,先来说一下理论概念,后期再通过实战来理解它,当前你只要知道有这三个东西就行:
DTS(Decoding Time Stamp),也就是解码时间戳,它会告诉播放器在什么时候来解码这一帧的数据.
PTS(Presentation Time Stamp),也就是显示时间戳,那么它也会告诉播放器在什么时候来显示这一帧的数据。
timebase时基:PTS值的真正时间单位,一般是秒;同时我们要注意在ffplay里面,PTS值的数据类型是double的,比如下面我定义了一个音频的PTS的变量并进行初始化:
double audio_pts = 0.020;
同时在ffplay里面,timebase的类型是结构体AVRational(其中一个结构体成员是表示分子,另外一个是表示分母!),具体代码如下:
typedef struct AVRational{
int num;
int den;
}AVRational;
那么通过这个如何来计算出PTS的显示时间呢?
比如说timebase={1,100},表示100毫秒,如果pts=1000,那么1000*1/100=10秒,意思就是这帧数据需要在第10秒的时候被显示出来!
因此在ffmpeg里面有一个关于把AVRational转变成double类型的接口实现,我们利用这个接口来计算pts的显示时间就非常方便了:
![34ee28ccfa3a5d3839c0de3e4761372a.png](https://i-blog.csdnimg.cn/blog_migrate/3ddd39d89088b31bb0d5b669814eb04e.png)
/**
* Convert an AVRational to a `double`.
* @param a AVRational to convert
* @return `a` in floating-point form
* @see av_d2q()
*/
static inline double av_q2d(AVRational a){
return a.num / (double) a.den;
}
所以显示时间戳的时间计算就等于:
pts * av_q2d(参数);
二、常用的音视频同步策略理论解决:
一般有三种解决方法:
(1):以音频为基准,同步视频到音频;如果视频慢了,就丢弃部分视频帧(会出现跳帧现象!);如果视频快了,就继续渲染上一帧。
(2)以视频为基准,同步音频到视频;如果音频慢了,则加快播放速度,或者也丢弃部分视频帧,不过会出现断音出现;如果音频慢了,就放慢播放速度,或者重复上一帧。但是这里改变播放速度,会涉及重采样的问题!
(3)以外部时钟为基准,同步音频和视频到外部时钟
三、总结:
今天的内容就简单分享到这里,更多的是咋们在实际开发过程中,如果遇到类似问题,该如何快速把这种问题解决掉才是关键,这也是需要咋们平时基础的积累!
下期,我们继续!