面试官: 谈下音视频同步原理,音频和视频能绝对同步吗?
作者:波哥
心理分析:音视频同步本身比较难,一般使用ijkplayer 第三方做音视频同步。不排除有视频直播 视频通话需要用音视频同步,可以从三种 音频为准 视频为准 自定义时钟为准三种方式实现音视频同步
求职者:如果被问到 放正心态,能回答多少是多少。如果你看了这篇文章肯定是可以回答上的
音视频的直播系统是一个复杂的工程系统,要做到非常低延迟的直播,需要复杂的系统工程优化和对各组件非常熟悉的掌握。下面整理几个简单常用的调优技巧:
以fflay来看音视频同步流程
ffplay中将视频同步到音频的主要方案是,如果视频播放过快,则重复播放上一帧,以等待音频;如果视频播放过慢,则丢帧追赶音频。
这一部分的逻辑实现在视频输出函数video_refresh中,分析代码前,我们先来回顾下这个函数的流程图:

在这个流程中,“计算上一帧显示时长”这一步骤至关重要。先来看下代码:
static void video_refresh(void *opaque, double *remaining_time)
{//……//lastvp上一帧,vp当前帧 ,nextvp下一帧last_duration = vp_duration(is, lastvp, vp);//计算上一帧的持续时长delay = compute_target_delay(last_duration, is);//参考audio clock计算上一帧真正的持续时长time= av_gettime_relative()/1000000.0;//取系统时刻if (time < is->frame_timer + delay) {//如果上一帧显示时长未满,重复显示上一帧*remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);goto display;}is->frame_timer += delay;//frame_timer更新为上一帧结束时刻,也是当前帧开始时刻if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)is->frame_timer = time;//如果与系统时间的偏离太大,则修正为系统时间//更新video clock//视频同步音频时没作用SDL_LockMutex(is->pictq.mutex);if (!isnan(vp->pts))update_video_pts(is, vp->pts, vp->pos, vp->serial);SDL_UnlockMutex(is->pictq.mutex);//……//丢帧逻辑if (frame_queue_nb_remaining(&is->pictq) > 1) {Frame *nextvp = frame_queue_peek_next(&is->pictq);duration = vp_duration(is, vp, nextvp);//当前帧显示时长if(time > is->frame_timer + duration){//如果系统时间已经大于当前帧,则丢弃当前帧is->frame_drops_late++;frame_queue_next(&is->pictq);goto retry;//回到函数开始位置,继续重试(这里不能直接while丢帧,因为很可能audio clock重新对时了,这样delay值需要重新计算)}}
}
这段代码的逻辑在上述流程图中有包含。主要思路就是一开始提到的如果视频播放过快,则重复播放上一帧,以等待音频;如果视频播放过慢,则丢帧追赶音频。实现的方式是,参考audio clock,计算上一帧(在屏幕上的那个画面)还应显示多久(含帧本身时长),然后与系统时刻对比,是否该显示下一帧了。
这里与系统时刻的对比,引入了另一个概念——frame_timer。可以理解为帧显示时刻,如更新前,是上一帧的显示时刻;对于更新后(is->frame_timer += delay),则为当前帧显示时刻。
上一帧显示时刻加上delay(还应显示多久(含帧本身时长))即为上一帧应结束显示的时刻。具体原理看如下示意图:

这里给出了3种情况的示意图:
- time1:系统时刻小于lastvp结束显示的时刻(frame_timer+dealy),即虚线圆圈位置。此时应该继续显示lastvp
- time2:系统时刻大于lastvp的结束显示时刻,但小于vp的结束显示时刻(vp的显示时间开始于虚线圆圈,结束于黑色圆圈)。此时既不重复显示lastvp,也不丢弃vp,即应显示vp
- time3:系统时刻大于vp结束显示时刻(黑色圆圈位置,也是nextvp预计的开始显示时刻)。此时应该丢弃vp。
delay的计算
那么接下来就要看最关键的lastvp的显示时长delay是如何计算的。
这在函数compute_target_delay中实现:
static double compute_target_delay(double delay, VideoState *is)
{double sync_threshold, diff = 0;/* update delay to follow master synchronisation source */if (get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER) {/* if video is slave, we try to correct big delays byduplicating or deleting a frame */diff = get_clock(&is->vidclk) - get_master_clock(is);/* skip or repeat frame. We take into account thedelay to compute the threshold. I still don't knowif it is the best guess */sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));if (!isnan(diff) && fabs(diff) < is->max_frame_duration) {if (diff <= -sync_threshold)delay = FFMAX(0, delay + diff);else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD)delay = delay + diff;else if (diff >= sync_threshold)delay = 2 * delay;}}av_log(NULL, AV_LOG_TRACE, "video: delay=%0.3f A-V=%f\n",delay, -diff);return delay;
}
上面代码中的注释全部是源码的注释,代码不长,注释占了快一半,可见这段代码重要性。
这段代码中最难理解的是sync_threshold,画个图帮助理解:

图中坐标轴是diff值大小,diff为0表示video clock与audio clock完全相同,完美同步。图纸下方色块,表示要返回的值,色块值的delay指传入参数,结合上一节代码,即lastvp的显示时长。
从图上可以看出来sync_threshold是建立一块区域,在这块区域内无需调整lastvp的显示时长,直接返回delay即可。也就是在这块区域内认为是准同步的。
如果小于-sync_threshold,那就是视频播放较慢,需要适当丢帧。具体是返回一个最大为0的值。根据前面frame_timer的图,至少应更新画面为vp。
如果大于sync_threshold,那么视频播放太快,需要适当重复显示lastvp。具体是返回2倍的delay,也就是2倍的lastvp显示时长,也就是让lastvp再显示一帧。
如果不仅大于sync_threshold,而且超过了AV_SYNC_FRAMEDUP_THRESHOLD,那么返回delay+diff,由具体diff决定还要显示多久(这里不是很明白代码意图,按我理解,统一处理为返回2*delay,或者delay+diff即可,没有区分的必要)
至此,基本上分析完了视频同步音频的过程,简单总结下:
- 基本策略是:如果视频播放过快,则重复播放上一帧,以等待音频;
- 如果视频播放过慢,则丢帧追赶音频。
- 这一策略的实现方式是:引入frame_timer概念,标记帧的显示时刻和应结束显示的时刻,再与系统时刻对比,决定重复还是丢帧。
- lastvp的应结束显示的时刻,除了考虑这一帧本身的显示时长,还应考虑了video clock与audio clock的差值。
- 并不是每时每刻都在同步,而是有一个“准同步”的差值区域。
在面试过程中感觉对音视频NDK底层这一块的知识点掌握好,于是针对这块知识点进行了梳理整理,并将面试中遇到的一些问题,也在其中找到了想要的答案,想了解恶补音视频开发这块知识的小伙伴可以参考这《音视频开发核心笔记》:https://qr18.cn/Ei3VPD
相关文章:
面试官: 谈下音视频同步原理,音频和视频能绝对同步吗?
作者:波哥 心理分析:音视频同步本身比较难,一般使用ijkplayer 第三方做音视频同步。不排除有视频直播 视频通话需要用音视频同步,可以从三种 音频为准 视频为准 自定义时钟为准三种方式实现音视频同步 求职者:如果被问到 放正心态…...
CFS三层靶机安装与配置
CFS三层靶机安装与配置 环境下载 百度网盘 提取码:Chen 环境安装 下载完成后,有三个文件夹,每个文件夹对应一个靶机 进入三个文件夹,双击打开后缀为.ovf的文件,按提示安装虚拟机 环境配置 网段划分 target1&#…...
爬虫入门教程-Spider
Spider 爬虫是定义如何抓取某个网站(或一组网站)的类,包括如何执行抓取(即关注链接)以及如何从其网页中提取结构化数据(即抓取项目)。换句话说,Spider是您定义用于为特定网站&#x…...
Python|蓝桥杯进阶第二卷——贪心
欢迎交流学习~~ 专栏: 蓝桥杯Python组刷题日寄 蓝桥杯进阶系列: 🏆 Python | 蓝桥杯进阶第一卷——字符串 🔎 Python | 蓝桥杯进阶第二卷——贪心 💝 Python | 蓝桥杯进阶第三卷——动态规划(待续…...
Chrome开发使用技巧总结
Chrome一个程序员开发神器,但是好多猿子们不会或者没有正确使用。今天教大家如何利用它快速高效的开发调试工作。代码格式化有很多css/js的代码都会被 minify 掉,你可以点击代码窗口左下角的那个 { } 标签,chrome会帮你给格式化掉。强制DOM状…...
你真的会在阳光下拍照片么?
你好,我是小麥。 上节课我们讲了如何通过影子判断光的质量,也就是光的软硬,这节课我们来接着说一说光的方向和环境光的实际运用。 虽然在现实生活里,我们可能没有从软硬的角度观察过光线,但我相信你在拍照片的时候一…...
量化择时——均线策略及改进方法(第1部分—因子测算)
文章目录道氏理论个股股价走势阶段板块、行业股价走势均线策略交易逻辑均线策略效果测算改进一:设置策略信号偏移量改进二:生成止盈止损信号道氏理论 使用盘面数据,根据计算出的一条或多条均线,判断入场与离场的时机,…...
封装几个有用的 Vue3 组合式API
本文将介绍如何使用Vue3来封装一些比较有用的组合API,主要包括背景、实现思路以及一些思考。 就我自己的感觉而言,Hook与Composition API概念是很类似的,事实上在React大部分可用的Hook都可以使用Vue3再实现一遍。 为了拼写方便,下文内容均使用Hook代替Composition API。相…...
MyBatisPlus中的条件构造器Wrapper
引言为什么要了解Wrapper?Wrapper解决的了什么问题?一、Wrapper:条件构造抽象类,用来解决单表操作出现的一些复杂问题,例如排序,和模糊查询等等结构图文字解释AbstractWrapper : 用于查询条件封装ÿ…...
类和对象及其构造方法
类和对象 现实世界的事物由什么组成? 属性 行为 类也可以包含属性和行为,所以使用类描述现实世界事物是非常合适的类和对象的关系是什么? 类是程序中的“设计图纸” 对象是基于图纸生产的具体实体什么是面向对象编程? 面向对象编…...
HStream Console、HStreamDB 0.14 发布
近两个月,HStreamDB 相继发布了 0.13 和 0.14 版本,包含多项已知问题修复。同时,我们也发布了全新的 HStream Console 组件,为 HStreamDB 带来了简洁友好的图形化管理界面,将帮助用户更轻松地使用和管理 HStreamDB. H…...
参考文献怎么查找,去哪里查找?一篇文章讲明白这些问题
在我们撰写论文查找参考文献时,往往不知道从哪里入手,本文小编就针对下面这三个方面给大家详细讲解下: 一、查找参考文献方法 二、参考文献资料查找网站 三、参考文献格式规范 一、查找参考文献方法: 1、知网全球最大的中文数据…...
docker-compose+HAProxy+Keepalived搭建高可用 RabbitMQ 集群
基础环境准备 系统环境:Centos7.6 Docker version: 1.13.1, build 7d71120/1.13.1 Docker Compose version: v2.2.2 三个节点: 10.10.11.79 (这一台做rabbitmq集群根节点) 10.10.11.80 (这台做haproxyke…...
自动化框架如何搭建?让10年阿里自动化测试老司机帮你搞定!自动化测试脚本怎么写?
一、何为框架?何为自动化测试框架? 无论是日常技术交流,还是在自动化测试实践中,经常会听到一个词叫:框架。之前对“框架”这个词知其然不知其所以然。现在看过一些资料以及加上我自己的一些实践有了我自己的一些看法…...
剑指 Offer 15. 二进制中1的个数
剑指 Offer 15. 二进制中1的个数 难度:easy\color{Green}{easy}easy 题目描述 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).…...
CHAPTER 3 磁盘管理
磁盘管理1 磁盘管理1.1 块设备信息(lsblk)1.2 挂载硬盘1.2.1 挂载单个硬盘(mkfs、mount)1.2.2 磁盘分区工具(fdisk)1.2.3 创建分区1.2.4 相关命令1. df2. partprobe3. mkfs1.3 逻辑卷管理器(LVM)1. 涉及概念2. 使用LVM流程1.4 磁盘检测及修复(fsck)1 磁盘…...
MS python学习(7)
Managing Keys - dotenv Managing keys usage of .env module 项目地址:https://github.com/theskumar/python-dotenv Reads the key,value pair from .env and adds them to environment variable. 将key明文(hard code)形式写在script里…...
工业物联网“杀手级”应用—预测性维护
一、预测性维护的必要性 随着新一轮科技革命和产业变革的兴起,工业物联网、大数据、人工智能等技术正与经济社会各领域加速渗透融合。由于市场竞争对精细化成本管控的要求,设备的重要性越来越凸显,设备的维护对策也必然从响应式维护…...
Java代码弱点与修复之——Explicit null dereferenced(显式空间接引用)
弱点描述 Explicit null dereferenced, 显示空间接引用。是 Coverity 静态代码分析工具检测到的一种中风险缺陷。这种缺陷通常发生在尝试使用空指针引用调用对象上的方法或访问属性时。 Explicit null dereferenced的缺陷可能会导致程序崩溃或产生不可预测的结果。 在Java语…...
一元导数与多元求导数总结
前序:文章结构 1.一元导数 ①一般函数求导 因为太简单的原因,事实上一般函数求导不会单独出现,大多数都是出现在各种特殊的求导过程中。只要掌握16个基本求导公式没问题。 ②复合函数求导(主要链式法则) 这种一般是…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
