
Mediabunny 作者发现 WebCodecs 音视频同步问题
来源标题:Project Sync: The journey to sample-perfect audio/video sync with WebCodecs
Mediabunny 作者在测试中发现,基于 WebCodecs 转码 Big Buck Bunny 片段时音频实际存在延迟,并非完美同步。文章由此展开对音视频同步链路的排查,指出媒体管线中任何环节出错都会破坏同步。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
你可能以为如今音视频同步(AV sync)基本已经是个解决的问题了——你想得没错!但它同时也非常脆弱,媒体处理链路上任何一个环节都必须正确工作才能维持同步。任何一处失误或疏忽都可能破坏它。
Mediabunny 以高质量为傲,我长期以来都以为它已经完全搞定了 AV sync。毕竟,它经过数百万个新媒体文件的实战检验,而且它基于 WebCodecs 来做音频编解码。WebCodecs 是由行业专家和浏览器开发者共同打造的 API,这些人搞媒体少说也有几年、多则几十年,对 AV sync 了如指掌。但假设终究只是假设,如果你想做出完美的东西,光靠假设是不够的。那我们就来实测一下!我们取 Big Buck Bunny 中一段带有辨识度音效的片段,用 Mediabunny 转码。来听听看:
在我看来画面和声音都很完美!我们能清楚地看到那只可怜的蝴蝶被苹果砸中的瞬间,正好同时听到撞击的音效。但实际上它并不同步!音频其实是延迟的——我们察觉不到,只是因为这个延迟低于人类能感知到的「音频滞后于视频」的 125 毫秒阈值。没错,这个数字是有人实际测量过的!有意思的是,音频超前于视频的情况要糟糕得多,仅仅 45 毫秒就能被察觉。我毫无根据的猜测是:人类进化出了正确感知延迟音频的能力,因为现实中的声速慢得离谱,所以现实生活本身也会「失同步」!
如果我们把这个转换连续做 64 次,每次都把上一次的输出喂给下一次的输入,延迟就变得非常明显了:除了画质有所下降之外,AV sync 现在歪得离谱。音频要过两秒多才出来,可怜的蝴蝶早就死透了,我们才听到苹果落地的声音。媒体文件的时长也从最初的五秒变成了将近九秒。
那么,这个延迟到底从哪来的?肯定不是 Mediabunny 造成的,因为 Mediabunny 只是读取文件,把音频包传给音频解码器,再把解码器的输出传回音频编码器,最后把编码器的输出封装成最终的媒体文件。音频解码器和编码器用的都是 WebCodecs,时间戳也没有被改动——所以你自然会以为这样一次「解码→编码」的往返应该是规规矩矩的。
让我们深入看看编码器对我们的信号做了什么。我们用一个简单的测试信号,这里用的是一段正弦波,频率在 1 秒内从 20 Hz 通过对数空间插值变化到 100 Hz,采样率 48 kHz:我们在 Mac 上用 WebCodecs 的 AAC AudioEncoder 对它编码,乍看之下似乎一切正常。但如果检查包的元数据,就能发现不对劲:第一个包从 0.0 秒开始,和我们的信号一致,但最后一个包结束于 1.066666 秒——也就是说编码器似乎给我们的信号多加了约 0.066 秒!如果我们把编码结果实际解码出来,就能看到信号变成了什么样:编码器不仅拉长了我们的信号,还把它延迟了!而且每次对这个信号做一次往返,它就会再延迟一次。这是经过八次往返之后的样子:如果我们把原始信号和编码后的信号做互相关,会发现它们在正好 44 毫秒的偏移处相关性最高,也就是说编码后的信号比输入滞后了 44 毫秒。在 48 kHz 下,这正好是 2112 个音频采样点的延迟。但为什么会这样?
原因是我们成了未补偿的编码器预热延迟(encoder priming delay)的受害者。音频编解码器会竭尽全力减少编码给定信号所需的比特数,虽然它们中许多并没有「关键帧」的概念(也就是说你可以从文件的任意位置开始解码音频),但在你开始解码之后,它们确实需要一段时间才能收敛到正确的输出。这意味着之前包的信息会影响当前包的解码方式。就 AAC 而言,编解码器的做法其实相当简单:它把信号拆成 1024 个采样点的 AAC 帧,每一帧都用「改进离散余弦变换」(MDCT)压缩编码,而相邻的两个 MDCT 块必须重叠才能产生正确的结果。这就是为什么你需要 2048 个采样点那么多的包才能准确重建后 1024 个采样点。
FFmpeg 的 AAC 编码器实际上只添加了最低必需的 1024 个采样点延迟。而我们上面实验用的 Apple AAC 编码器则添加了 2112(1024 + 1024 + 64)个采样点的延迟。据我所知,他们为什么选这个具体数字,没有任何地方有明确记载。
所以,音频编码器需要添加延迟来保持输入信号完整,但我们同时希望它和视频保持同步。有两种显而易见的方案:据我所知,几乎所有编码器都选择第一种方案,因为它只增加数据、不删除任何数据。所以 WebCodecs 在开头添加一些静音其实是正确的——如果它不这么做,我们就会丢失信号的开头!不正确的地方在于,它假装这个信号从时间戳 0 开始。我认为这是 WebCodecs 规范及其实现中的一个疏漏(而且所有实现都搞错了这一点)。GitHub 上有一个关于这个话题的 issue,但讨论已经持续了三年多。
等待 WebCodecs 团队修订规范、再等浏览器厂商跟进实现并发布,对 Mediabunny 来说不是一个选项。我要它现在就能产出正确的文件。于是我启动了 Project Sync,这是我为彻底修复 Mediabunny 中 AV sync 缺陷所做的努力。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。