Sill 音频管线深度剖析:AAC-ELD 128kbps 编码、10ms 分片与逐包漂移校正的精密同步机制

发布时间:2026/11/4 15:54:26
Sill 音频管线深度剖析:AAC-ELD 128kbps 编码、10ms 分片与逐包漂移校正的精密同步机制

【免费下载链接】sill

Any window from your Mac, live on your iPhone or iPad. Touch, Apple Pencil, trackpad and keys. Free, open source, no servers.

项目地址:https://gitcode.com/gh_mirrors/sill1/sill
点击查看免费下载

Sill 是一款免费、开源的 Mac 到 iPhone / iPad 串流工具,把 Mac 上的任意窗口或整个桌面实时投到你的移动设备上,支持触控、Apple Pencil、触控板和键盘操作,且全程无需自建服务器。它的音频功能(Send Audio)会把 Mac 上被串流应用的声音同步送到设备端播放——声音永远滞后画面一点点,但绝不会提前。本文带你拆解 Sill 音频管线背后的三个关键设计:AAC-ELD 128 kbps 编码、10 ms 音频分片,以及逐包漂移校正的音视频同步机制。

一、功能速览:Mac 的声音如何到达 iPad

在 Sill 的 Mac 端菜单或 设置 › Streaming 中打开Send Audio(Beta)后,Sill for Mac 会捕获当前串流应用(或整个桌面)的声音,编码后经本地网络发送到 iPhone / iPad 播放。它有两个重要特性(见 README.md):

  • 声音跟随画面:设备端播放的声音"片刻滞后于画面,但绝不超过画面";
  • 双端都有声:Mac 自己照常出声,每台设备可以用 Sound 按钮单独静音。

整条管线的蓝图写在设计文档 docs/audio-plan.md 中,Mac 端核心代码集中在 Sources/SillHost/,设备端播放模型在 iOSClient/AudioPlayout.swift。

二、为什么选择 AAC-ELD 128 kbps 编码

编码器的选择不是拍脑袋,而是用真实信号实测出来的。Sill 团队把一段 6 秒的忙碌信号(噪声叠加三个音调)分别送入 AudioToolbox 的各款编码器,测量"端到端延迟"(从突发信号进入编码器到离开解码器的时间):

编码器每包帧数端到端延迟(10 ms 分片)编码/解码 CPU(单核)
AAC-LC1024(21.3 ms)74.7 ms0.49% / 0.05%
AAC-LD512(10.7 ms)30.7 ms0.52% / 0.06%
AAC-ELD(480 帧)480(10.0 ms)15.0 ms0.52% / 0.07%
Opus(10 ms)48016.5 ms0.95% / 0.32%

AAC-ELD 胜出的理由很清晰(docs/audio-plan.md "Which codec: measured"):

  • 延迟最低:AAC-LC 光固有延迟就有 65–75 ms,比画面整条链路还长;AAC-ELD 只有 15 ms 出头;
  • CPU 更省:同样延迟下,编码 CPU 约为 Opus 的一半,解码只有其五分之一;
  • 生态成熟:AirPlay 镜像传输的正是 AAC-ELD,是 Apple 自己的选择。

最终参数固定为:48 kHz、双声道、128 kbps,见 AudioEncoder.swift 中的三个常量。编码由 AudioToolbox 的AudioConverter完成,每块输入正好产出一个数据包。

三、10 ms 分片:把 PCM 切成 480 帧一包的节奏

声音捕获走的是一条独立的、只含音频的 SCStream(AudioCapture.swift 中capturesAudio = true,画面尺寸设为 2×2 每秒 1 帧后直接丢弃)。这样做的好处是:画面管线重启(旋转、缩放、切换编码器)时声音不受影响,声音失败也只影响声音本身。

捕获来的 PCM 块交给纯函数式的AudioPacketizer(AudioPacketizer.swift),它做三件事:

  1. 定包长:看第一个音频块的帧数——是 512 的整数倍就用 512 帧一包(10.7 ms),否则用 480 帧一包(正好10 ms)。能整除捕获块的分片方式端到端快 9–10 ms;
  2. 打时间戳:每个块的时间戳取自"该包解码后首个样本对应输入帧"的主机时钟时刻。由于编码器有 240/256 帧的固有延迟(priming),时间戳要往前补偿这段距离;
  3. 断段保护:音频块迟到或早到超过 1/4 块(约 2.5 ms),就判定捕获出了断口——旧段直接丢弃,不补齐、不填零,从这一块开始新的一段。因为"为过去的帧现在造一个包"只会让设备端误以为链路抖动,白白抬高缓冲。

编码后的每个包立刻转成 kind 43 消息发往网络(StreamMessage.swift),时间戳在发送瞬间由主机时钟换算成墙上时钟(AudioPipeline.swift),与视频帧的打戳方式完全一致。

四、不用时钟同步:一个"地板值"锚定两台设备

这里是最精巧的部分。两台设备的时钟会互相漂移(每秒差几十微秒级),传统做法要做 NTP 式时钟同步——Sill 完全没做。它的方案只需要一个稳定的参照物,而不是绝对时间:

地板值(floor)= 最近 10 秒内所有音频包(到达时刻 − 时间戳)的最小值。

原理:网络延迟会波动,但"最快的那一路"近似稳定。一个时间戳为 T 的音频包,应在设备单调时钟的T + floor + delay时刻被听到(AudioPlayout.swift)。两个时钟慢慢漂移时,这个滑动最小值会自动跟着漂移走,声音永远被"锚"在画面旁边。

delay取两项的较大值:

  • need:抖动覆盖(最近 10 秒内到达 − 时间戳 − floor的最大值,在家 20–150 ms 区间,外出 60–600 ms)+ 一个包(10 ms)+ IO 缓冲 + 输出延迟 + 2 ms 余量;
  • 画面守卫:最近 2 秒视频帧的中位滞后 + 1.5 个刷新 + 4 ms,再多加一帧。这条规则保证了铁律——声音永远不会跑到画面前面。

五、逐包漂移校正:每秒 ±1 帧的"微调术"

时钟漂移不会一次到位,所以模型每秒比较一次"正在播放的样本应在哪"与"它实际在哪",然后每个数据包最多增删 1 帧(AudioPlayout.swift):

  • 声音快了 → 在该包最安静的点复制一帧插入(取相邻均值,听不出痕迹);
  • 声音慢了 →删掉一帧(保留与前一帧的均值);
  • 幅度约 2 ms/秒,远大于时钟漂移速度(0.1 ms/秒),完全不可闻。

小误差用微调消化,大误差另走"跳变"通道:一旦误差超过 40 ms(链路停顿、路由切换),或声音即将越过画面,就立即丢包或插入静默完成对齐,并配 5 ms 淡入淡出(iOSClient/AudioPlayout.swift 常量jumpOver = 0.040)。若连续一秒的包都悬在地板值上方 50 ms 以上(或新地板突然低 20 ms),则判定 Mac 时钟跳变,整个窗口重新开始统计。

其他细节同样讲究:最近 256 个(epoch, seq)去重、断点后整包丢弃再淡入、10 秒无声自动停掉音频引擎等,都写在这份纯 Foundation 模型里,可用模拟时钟单独跑测试(Tests/checks/audio-playout/)。

六、声音与画面共享同一条链路:音频优先

声音和视频走同一条 TCP 连接,但规则是"声音不让路,画面让路":

  • 在家时,音频消息不计入画面的在途计数(inflight),不会被丢帧规则误伤;
  • 外出(Remote Access)时,音频字节计入远程预算,拥塞发生时paceRemote只丢视频帧、从不丢声音——链路扛不住时画面降帧等关键帧,声音继续保持约 16 KB/s 稳定流出;
  • 音频在独立队列编码、生成即发(AudioPipeline.swift),一帧画面也从不为声音等待。

设备端的诊断 HUD 上还能看到sound XX字样,表示声音当前滞后画面多少毫秒(DiagnosticsHUD.swift)。

七、如何开启并验证

  1. Mac 端:Sill 状态菜单或 设置 › Streaming 中打开Send audio(Beta);CLI 宿主可用--audio启动参数;
  2. 设备端:顶部工具栏出现 Sound 按钮,点击静音/恢复仅影响本设备,无需 Mac 确认;
  3. 观察同步效果:开一个有清晰鼓点的应用,用诊断 HUD 查看sound滞后值——典型约 50 ms,符合"1–2 帧之后"的设计目标。

八、核心模块速查

  • 设计文档:docs/audio-plan.md
  • 捕获(音频专属 SCStream):Sources/SillHost/AudioCapture.swift
  • 分片与打戳:Sources/SillHost/AudioPacketizer.swift
  • AAC-ELD 编码器:Sources/SillHost/AudioEncoder.swift
  • 管线编排:Sources/SillHost/AudioPipeline.swift
  • 线上协议(kind 43):Sources/StreamProtocol/Audio.swift
  • 设备端同步模型:iOSClient/AudioPlayout.swift
  • 纯逻辑测试:Tests/checks/audio-packetizer/、Tests/checks/audio-playout/

一句话总结:Sill 用"最小编码延迟 + 10 ms 定长分片 + 滑动地板值 + 逐包 ±1 帧微调"四步组合,在没有时钟同步的前提下把 Mac 的声音稳稳锚在画面之后——这正是开源项目少见的、以实测数据驱动的精密同步工程。

【免费下载链接】sill

Any window from your Mac, live on your iPhone or iPad. Touch, Apple Pencil, trackpad and keys. Free, open source, no servers.

项目地址:https://gitcode.com/gh_mirrors/sill1/sill
点击查看免费下载
上一篇:WebXR沉浸式体验:使用Spark构建跨设备3D交互场景
下一篇:Armchair与StoreKit Review Prompt:iOS 10.3+评分功能对比

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考