ffplay源码分析(六)音视频同步

本文解析了ffplay中音视频不同步的原因,如设备差异、编码延迟等,并介绍了音视频同步的基本策略,重点讲解了视频同步音频的方法,包括使用audio基准、video基准以及外部时钟基准。同时,详细剖析了ffplay中如何通过compute_target_delay函数来计算延迟并调整播放速度以实现同步。

一、音视频为什么会不同步?

在音视频处理中,经常会出现不同步的现象,常见的原因如下

  1. 录制设备差异:音频和视频可能使用不同的设备进行录制,这可能会导致录制的音频和视频之间存在时间差。

  2. 编码和传输延迟:音频和视频在编码、传输和解码过程中可能会引入延迟,导致音频和视频不同步。

  3. 系统时钟漂移:音频和视频设备可能使用不同的系统时钟,导致时钟漂移,从而影响音视频同步。

  4. 解码和渲染延迟:音频和视频在解码和渲染过程中可能会产生不同的延迟,导致不同步。在ffplay中,音视频的输出有自己的线程。音频的输出线程是sdl的回调线程,视频的输出是程序的主线程。这两个线程调度过程中不一定那么准确。

二、音视频同步策略

小时后很多同学都有电子手表。当发现自己的手表和别人的手表的时间不一样的时候,我们会去对表。
音视频同步就是一个对表的过程。
但是大家的时钟都走的好好的,以谁的为准呢?
无所谓,只要设定一个基准,大家一致就行了。
在ffplay中,根据基准的不同,三种同步方式,分别以不同的时间为基准。

  • 以音频为基准,同步视频到音频(AV_SYNC_AUDIO_MASTER)

    • 视频慢了,丢帧或快放
    • 视频快了,继续渲染上一帧
  • 以视频为基准,同步音频到视频(AV_SYNC_VIDEO_MASTER)

    • 音频慢了,加快播放速度(或丢弃部分帧,但极容易听出断音)
    • 音频快了,降低播放速度(或重复上一帧)
  • 以外部时钟为基准,同步音频和视频到外部时钟(AV_SYNC_EXTERNAL_CLOCK)

    • 选择一个外部时钟为基准,视频和音频的播放速度都以该时钟为标准。

这三种是最基本的策略,考虑到人对声音的敏感度要强于视频,一般情况下我们会采取视频去同步音频的策略。

但实际还是要根据情况调整的。比如一个没有声音的视频,或者刚播放的只有音频没有视频,视频比较晚到,那前面的音频也可以先丢弃。

三、ffplay中的音视频同步

由于大部分的场景都是视频去同步音频,其它的几乎不怎么用。
所以这里只看视频同步音频的方法。
ffplay中的音视频同步主要是在video_refresh中去同步的。

video_refresh 是刷新视频帧的结构体,分析这个函数的时候,先不用考虑音视频同步,只要考虑一帧需要显示多久

//事件循环
static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {
   
   
    double remaining_time = 0.0;
    //收集事件
    SDL_PumpEvents();
    //检查是否有事件
    while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
   
   
       //隐藏鼠标的操作
        if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {
   
   
            SDL_ShowCursor(0);
            cursor_hidden = 1;
        }
        if (remaining_time > 0.0)
            av_usleep((int64_t)(remaining_time * 1000000.0));
        //
        remaining_time = REFRESH_RATE;
        if (is->show_mode != SHOW_MODE_NONE && (!is->paused || is->force_refresh))
            video_refresh(is, &remaining_time);
        SDL_PumpEvents();
    }
}


/* called to display each frame */
static void video_refresh(void *opaque, double *remaining_time)
{
   
   
    VideoState *is = opaque;
    double time;

    Frame *sp, *sp2;

    if (!is->paused && get_master_sync_type(is) == AV_SYNC_EXTERNAL_CLOCK && is->realtime)
        check_external_clock_speed(is
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值