以前工作中参与了一些音视频程序的开发,不过使用的都是芯片公司的SDK,没有研究到更深入一层,比如说音视频同步是怎么回事。只好自己抽点时间出来分析开源代码了,做音视频编解码的人都知道ffmpeg,他在各种音视频播放软件当中已经使用很多了。当然,这里不是来分析音视频播放软件,如果真的想学习,自己可以研究一下ffmpeg自带的一个简单播放器ffplay,在这里不对ffplay做详细分析,只拿出来他的音视频同步一部分来详细分析(下面代码取自ffmpeg-0.5)。
      在ffplay里的视频图像更新是在一个timer里面更新的,当有timer事件时就会调用video_refresh_timer()函数,而在这个函数里面会调用compute_frame_delay()计算下一帧图像显示的时间
video_refresh_timer()
    /* launch timer for next picture */
    schedule_refresh(is, (int)(compute_frame_delay(vp->pts, is) * 1000 + 0.5));

compute_frame_delay()的参数frame_current_pts表示当前图像的pts,参数is是全局videoState结构指针。
static double compute_frame_delay(double frame_current_pts, VideoState *is){
    double actual_delay, delay, sync_threshold, ref_clock, diff;

    /* compute nominal delay */
    delay = frame_current_pts - is->frame_last_pts;      // frame_last_pts存着上一帧图像的pts,用当前帧的pts减去上一帧的pts,从而计算出一个估计的delay值
    if (delay <= 0 || delay >= 10.0) {                            // 这个delay值有一个范围,如果超出范围的话,则用上一次的delay值
        /* if incorrect delay, use previous one */
        delay = is->frame_last_delay;                               // frame_last_delay存放前面一次计算得到的delay值
    } else {
        is->frame_last_delay = delay;                               // 如果计算出来的值合法,则保存下来
    }
    is->frame_last_pts = frame_current_pts;                 // 将当前帧的pts保存下来

    /* update delay to follow master synchronisation source */                      // 更新delay值用于跟随主同步源
    if (((is->av_sync_type == AV_SYNC_AUDIO_MASTER && is->audio_st) ||
         is->av_sync_type == AV_SYNC_EXTERNAL_CLOCK)) {                          // 音频做主同步源或外部时钟做同步源时进入
        /* if video is slave, we try to correct big delays by
           duplicating or deleting a frame */
        ref_clock = get_master_clock(is);                                     // 得到主时钟源的当前时钟值做为参考时钟
        diff = frame_current_pts - ref_clock;                                // 当前帧的pts减去参考时钟,结果diff可能为正值,也可能为负值

        /* skip or repeat frame. We take into account the
           delay to compute the threshold. I still don't know
           if it is the best guess */
        sync_threshold = FFMAX(AV_SYNC_THRESHOLD, delay);  // delay和AV_SYNC_THRESHOLD之间取一个最大值
        if (fabs(diff) < AV_NOSYNC_THRESHOLD) {
            if (diff <= -sync_threshold)                                          // sync_threshold绝对是个>0的值
                delay = 0;                                                                 // 如果diff是个很小的负数,则说明当前视频帧已经落后于主时钟源了,下一帧图像应该快点显示,所以delay=0
            else if (diff >= sync_threshold)
                delay = 2 * delay;                                                     // 如果diff是一个比较大的正数,则说明当前视频帧已经超前于主时钟源了,下一帧图像应该延迟显示
        }
    }

    is->frame_timer += delay;                                                    // frame_timer是一个delay累加的值,
    /* compute the REAL delay (we need to do that to avoid
       long term errors */
    actual_delay = is->frame_timer - (av_gettime() / 1000000.0);  // frame_timer减去当前系统时钟,得到一个actual_delay值
    if (actual_delay < 0.010) {
        /* XXX: should skip picture */
        actual_delay = 0.010;
    }

#if defined(DEBUG_SYNC)
    printf("video: delay=%0.3f actual_delay=%0.3f pts=%0.3f A-V=%f\n",
            delay, actual_delay, frame_current_pts, -diff);
#endif

    return actual_delay;                                            // actual_delay将做为下一次帧图像更新的TIMER参数
}

****
1. 在ffplay当中并没有跳帧的作法,只是通过控制视频帧显示速度来实现音/视频同步的。
2. 一般情况下,音频不容易同步到视频,但视频比较容易同步到音频,因为音频输出的采样率等参数一般都是固定的,设定好以后消耗数据的速率基本是固定的,很难在播放过程当中动态调整,
   而视频显示的图像更新速率更容易调整些。通常可以通过加速刷新,跳帧等办法来调整。

既然同步的时候用到的系统时钟,那当pause/resume时又做了些什么来保证同步不出问题呢?这是需要考虑的一个重要问题,来看stream_pause()函数
static void stream_pause(VideoState *is)
{
    is->paused = !is->paused;    // 反转paused值
    if (!is->paused) {
        // 如果是resume操作,则要更新当前视频帧的pts
        is->video_current_pts = get_video_clock(is);
        
        // video_current_pts_time记录了上一帧视频图像显示时的系统时钟, av_gettime() - is->video_current_pts_time得到的结果刚好是pause这段时间间隔,通过这种方式保证了frame_timer永远记录的是ffplay启动后到当前时间点的时间间隔
        is->frame_timer += (av_gettime() - is->video_current_pts_time) / 1000000.0;
    }
}

好了,是不是对音视频同步有了一定认识呢。这里只做了一点点简单的分析,我的分析不一定准确,只做参考学习,如果有人发现问题,请告诉我。ffplay的代码实现很完整,如果有兴趣,可以自己尝试去分析一下音频同步到视频的或音/视频同步到外部时钟的实现。

 
 
转自:http://blog.chinaunix.net/uid-20364597-id-3510052.html

(转)ffplay的音视频同步分析之视频同步到音频的更多相关文章

  1. ffplay源码分析4-音视频同步

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10307089.html ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg ...

  2. Android IOS WebRTC 音视频开发总结(六六)-- 三个角度分析美女视频直播这个行业

    本文主要从用户,公司和技术角度分析美女视频直播这个行业,文章最早发表在我们的微信公众号上,支持原创,详见这里, 欢迎关注微信公众号blackerteam,更多详见www.rtc.help 美女视频直播 ...

  3. 【转】基于DM8168的视频智能分析系统的设计方案

        [导读] 为了实现高清视频的智能分析功能,本文介绍了一种以TI公司的DM8168为核心的高清视频智能分析系统的设计方案,该方案从硬件设计和软件设计两个方面介绍了硬件组成.工作流程.软件架构,并 ...

  4. Android逆向之旅---静态方式分析破解视频编辑应用「Vue」水印问题

    一.故事背景 现在很多人都喜欢玩文艺,特别是我身边的UI们,拍照一分钟修图半小时.就是为了能够在朋友圈显得逼格高,不过的确是挺好看的,修图的软件太多了就不多说了,而且一般都没有水印啥的.相比较短视频有 ...

  5. EasyNVR智能云终端接入AI视频智能分析功能,用户可自定义接入自己的分析算法

    视频分析的需求 人工智能的发展和在行业中的作用就不用多说了,已经到了势在必行的一个程度了,尤其是对于流媒体音视频行业来说,这基本上是人工智能重中之重的领域,视频人工智能,也就是视频视觉分析的应用方式大 ...

  6. m3u8视频格式分析

    “ 学习m3u8格式.” 一段时间之前,乘着某美女CEO的东风,学习了一个新的数据格式,即m3u8格式. 经过一段时间的沉淀,美女CEO的热潮大概已经褪去,今天才对这个格式进行分析,嘻嘻. 先介绍下来 ...

  7. Chromium Graphics: GPUclient的原理和实现分析之间的同步机制-Part II

    摘要:Part I探析GPUclient之间的同步问题,以及Chromium的GL扩展同步点机制的基本原理.本文将源码的角度剖析同步点(SyncPoint)机制的实现方式. 同步点机制的实现主要涉及到 ...

  8. 仿爱奇艺视频,腾讯视频,搜狐视频首页推荐位轮播图(二)之SuperIndicator源码分析

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52510431 背景:仿爱奇艺视频,腾讯视频 ...

  9. python爬取B站视频弹幕分析并制作词云

    1.分析网页 视频地址: www.bilibili.com/video/BV19E… 本身博主同时也是一名up主,虽然已经断更好久了,但是不妨碍我爬取弹幕信息来分析呀. 这次我选取的是自己 唯一的爆款 ...

随机推荐

  1. appium +uiautomator2 遇到 deviceName can't be blank 提示的解决

    为了获取android toast ,需要升级selenium 到 3.4.0 和appium 5.0.0-BETA9 到最新版本,并在启动代码前面追加一行代码: capabilities.setCa ...

  2. 并发编程之socketserver模块

    一.socketserver模块介绍 基于tcp套接字,关键的就是两个循环,一个是链接循环,一个是通信循环 socketserver模块中分两大类:srever类(解决链接问题)和request类(解 ...

  3. HTML5背景音乐的暂停与播放

    HTML代码:     <audio id="myaudio" loop="loop" preload="auto" autoplay ...

  4. sudo日志记录记录(rsyslog)

    1,查软件 rpm -qa|egrep "sudo|rsyslog" 2,编辑sudoers echo "Defaults logfile=/var/log/sudo.l ...

  5. linux三剑客grep|sed|awk实践

    最好先学习正则表达式的基本用法,以及正则表达式BREs,EREs,PREs的区别 此坑待填 grep sed awk

  6. org.hibernate.HibernateException: Could not parse configuration: /hibernate.cfg.xm

    org.hibernate.HibernateException: Could not parse configuration: /hibernate.cfg.xm 检查jar包是否正确以及配置的xm ...

  7. 【转】PowerDesigner表结构和字段大小写转换

    [转自]http://blog.csdn.net/xysh1991/article/details/8016192 使用方法:进入PowerDesigner,打开一个PDM,在菜单栏找到:Tools ...

  8. SQLSERVER NULL值判断

    sqlserver 在判断数据条件时,如果数据包含null的话则永远为false,null不参与判断,可以使用isnull(列,默认值)来判断null值的数据列,或者列 is null or 列的条件 ...

  9. Python中关于类和函数的初体验之"__init__"和"__str__"不是"_init_"和"_str_"

    刚刚接触Python,今天就是怎么也调试不过去了,上网上查直到晚上才查到一个有效信息,真是坑啊!原来Python中的这些“魔法”方法的命名里就有陷阱…… 上图中的那两个红圈圈,一定要记住哦,这些Pyt ...

  10. range() 函数详解 python

    使用python的人都知道range()函数很方便,今天再用到他的时候发现了很多以前看到过但是忘记的细节.这里记录一下range(),复习下list的slide,最后分析一个好玩儿的冒泡程序. 这里记 ...