音视频同步实现理论
PTS和time_base
PTS即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。time_base即时间度量单位(时间基),可以类比:米、千克这种单位。
分别获取音频和视频的PTS(播放时间戳):
PTS = avFrame->pts * av_q2d(avStream->time_base);
获取音视频PTS差值,根据差值来设置视频的睡眠时间达到和音频的相对同步。视频快了就休眠久点,视频慢了就休眠少点,来达到同步。
在 Video.h 中声明一个 audio 成员变量和 clock 成员属性用来保存当前视频的时间戳 并且声明一个函数用来计算音频视频的差值
//音频对象
WlAudio *audio = NULL;
//时钟
double clock = 0;
double getFrameDiffTime(AVFrame *avFrame);
在 Video.cpp 中写方法声明
double WlVideo::getFrameDiffTime(AVFrame *avFrame) {
//返回当前avFrame的显示时间戳
double pts = av_frame_get_best_effort_timestamp(avFrame);
if (pts == AV_NOPTS_VALUE) {
pts = 0;
}
pts *= av_q2d(time_base);
if (pts > 0) {
clock = pts;
}
double diff = audio->clock - clock;
return diff;
}
在 FFmpeg.cpp 中的 start 函数中将 audio 赋值给 video
//把video设置到视频当中
video->audio = audio;
在 video.cpp 渲染之前调用函数计算diff
double diff = video->getFrameDiffTime(avFrame);
根据音频和视频时间戳的差值增大或减小视频渲染的睡眠时长
在 video.h 中新建三个成员变量用来计算
//时钟
double clock = 0;
double delayTime = 0;
double defaultDelayTime = 0.004;
声明一个根据音视频时间戳差值 增大或减小视频渲染的睡眠时长的函数
double getDelayTime(double diff);
在 video.cpp 写这个函数的实现
double WlVideo::getDelayTime(double diff) {
if (diff > 0.003) {
delayTime = delayTime * 2 / 3;
if (delayTime < defaultDelayTime / 2) {
delayTime = defaultDelayTime * 2 / 3;
} else if (delayTime > defaultDelayTime * 2) {
delayTime = defaultDelayTime * 2;
}
} else if (diff < -0.003) {
delayTime = delayTime * 3 / 2;
if (delayTime < defaultDelayTime / 2) {
delayTime = defaultDelayTime * 2 / 3;
} else if (delayTime > defaultDelayTime * 2) {
delayTime = defaultDelayTime * 2;
}
} else if (diff == 0.003) {
}
if (diff >= 0.5) {
delayTime = 0;
} else if (diff <= 0.5) {
delayTime = defaultDelayTime * 2;
}
if (fabs(diff) > 10) {
//音频不存在
delayTime = defaultDelayTime;
}
return delayTime;
}
在渲染下面调用
av_usleep(video->getDelayTime(diff) * 1000 * 1000);