FFmpeg音视频同步

音视频同步本质就是一个你追我赶的过程 快的等一等慢的。慢的走快点去赶上快的。目前常见的同步方式有下面3种:

  • 1、以一个外部时间为基准,将音频与视频同步至此时间

  • 2、以视频为基准,音频去同步视频的时间

  • 3、以音频为基准,视频去同步音频的时间

以一个外部时间为基准,将音频与视频同步至此时间,因为人对声音是高度敏感的,所以通常都是以视频去同步音频。本文的实现也是以视频去同步音频的时间。具体来说就是当视频落后于音频,通过丢帧等方式来快速追赶上音频,当视频快于音频,通过休眠等一等音频。

相关知识原理:

  • DTS:解码时间戳,用来告诉解码器Packet的解码顺序
  • PTS:显示时间戳,表示从packet解码出来的的原始数据的显示顺序

在音频中DTS和PTS是相同的,但是视频中由于存在B帧(双向预测帧),会导致DTS和PTS不一定相同。所以我们通常使用PTS来确定音视频显示时间

要计算pts ,在FFmpeg 中还有个非常重要的概念需要理解。时间基 time_base

time_base可以理解为每一个刻度是多少时间。

举例来说,现在有一把尺子,总共有10个刻度,你能告诉我这把尺子代表的长度吗?不能吧,但是我告诉你每个刻度是1cm 那这把尺子就是 10cm,如果每个刻度是1m,那这把尺子就是10m。这样我们才能用它来衡量其他物体的长度,直接看其他物体占用了多少个刻度就好了。

具体来说,time_base 代表了时间刻度。如果把1s分为25份 那么 time_base 就是 1 / 25 代表每个刻度是0.04秒,而pts的单位就是时间刻度,代表占多少个时间刻度,我们用pts*time_base就可以计算pts的具体的时间了。

在FFmpeg中我们可以从流中获取time_base,他的类型是AVRational代表了分数

typedef struct AVRational{
    int num; // 分子
    int den; // 分母
} AVRational;

用分子除以分母就代表了每个时间刻度。

下面看具体核心代码:
1、在解封装的时候从流中获取time_base
音频

audio_channel = new AudioChannel(audio_stream,avCodecContext);
audio_channel->setTimeBase(avFormatContext->streams[audio_stream]->time_base);

对于视频还要额外获取一个fps

AVStream * stream = avFormatContext->streams[video_stream];
video_channel = new VideoChannel(video_stream, avCodecContext);
video_channel->setTimeBase(stream->time_base);
AVRational frame_rational = stream->avg_frame_rate;
int fps = av_q2d(frame_rational);
video_channel->setFps(fps);

2、在播放音频的时候,记录当前的相对播放时间

audio_clock = frame->pts * av_q2d(timeBase);

3、在播放视频,渲染每一帧的时候,通过audio_clock来判断谁快谁慢,再执行相应的操作。

 //解码耗时
double extra_delay = frame->repeat_pict / (2*fps);
double fps_delay = 1.0 / fps;
double real_delay = extra_delay + fps_delay;
double video_time = frame->pts * av_q2d(timeBase);
double audio_clock = audio_channel->audio_clock;
double diff_time = video_time - audio_clock;
    if (diff_time > 0){
        //video faster
        //本来每一帧之间就存在时间间隔,需要休眠real_delay,比如fps = 60,一秒应该播放60帧 每一帧就是1/60s。如果不延迟,有的机器解码,渲染速度很快,可能会大于60帧
        //现在又由于视频比音频快diff_time,那就再多休眠diff_time时间
        av_usleep((diff_time + real_delay) * 1000 * 1000);
    } else{
        //audio faster
        if (fabs(diff_time) >= 0.05){
            //丢帧
            av_frame_unref(frame);
            ReleaseAVFrame(&frame);
            frames.runSyncCallback();
            continue;
           }
     }

代码示例:github

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值