在 WWDC26 发布的 iPadOS 27 与 macOS Golden Gate 中,随航迎来了堪称史诗级的更新 —— 你可以通过 iPad 的触控屏,用手指完整操控随航中的 macOS。某种程度上这也印证了「Apple 即将推出触屏版 MacBook」的传闻。
若你已经加入 iPadOS 以及 macOS 的 Beta 计划,更新至 iPadOS 27 与 macOS Golden Gate 后即可直接在随航功能中体验到这一新特性。经笔者实测,当前 Beta 版本中触控的延迟几乎难以察觉,交互也足够流畅。不过由于 macOS App 大多针对的是键鼠的操作逻辑,大部分 App 尚未针对触控交互进行专门优化,目前大部分的点击与滑动操作,本质上只是由系统转译为相应的鼠标操作。
虽然在现阶段,相信你在体验一段时间后可能还是会更愿意回到传统的键鼠操作。但在高强度使用随航近两年后,笔者对它最深的印象其实只有两个字:无感。有时它的流畅甚至会让我忘记两台设备之间并没有数据线连接。
本文试着从协议、编码、硬件等技术层面入手,为你解析随航功能流畅体验背后的秘密。
什么是随航?
或许有些读者对随航还不太熟悉。简单来说,随航(Sidecar)就是通过无线或有线传输的方式,将你的 iPad 作为 Mac 的扩展显示屏。不需要复杂的 App 连接、也不需要数据线,只需打开 Mac 和 iPad 进行配对,Mac 便拥有了第二块屏幕。
你可以在 macOS 菜单栏中点击「控制中心 > 屏幕镜像」,然后在「镜像或扩展至」一栏中选择对应的 iPad 来开启随航。Mac 会通过蓝牙查找 iPad,然后以点对点 Wi-Fi 的形式传输画面。


你也可以前往「系统设置 > 显示器」中,在底部选择「镜像或扩展至 iPad」,然后从列表中选择一部 iPad。

连接完成后,iPad 会像普通外接显示器一样出现在 macOS 的显示器设置中,你可以调整它的位置、分辨率以及镜像或扩展方式。这里就不再赘言了。
除此之外,随航也针对 iPad 的特性进行了一些扩展,前文提到的「触控」特性便是其中之一。另外随航从推出之初便支持 Apple Pencil,你可以直接在 iPad 屏幕上使用 Apple Pencil 操作 Mac 应用;在兼容的应用中,压感、倾斜等笔触信息也能得到保留。从某种程度上说,开启随航的 iPad 可以当作一块带屏幕的数位板使用。
若想了解更为详细的内容,如具体的操作以及扩展功能,你可以访问 Apple 官网或查看少数派中有关随航的介绍。
在「随航」成为官方功能之前
从功能和体验层面来说,随航可以说是相当完善,但「让 iPad 变成 Mac 副屏」其实并不是 Apple 首创的想法——事实上,在第一代 iPad 正式发售的当天这一概念便已落地,并以一款 iPad「首发 App」的形式登陆 App Store。
它叫 iDisplay,由 SHAPE Services 开发。
iDisplay 会在 Mac 中安装一套扩展驱动,创建一块系统能够识别的虚拟显示器,再通过 Wi-Fi 将画面发送到 iPad。用户可以像使用普通副屏一样排列两块屏幕,把 Mac 窗口拖到 iPad 上;甚至在遥远的 2010 年,iDisplay 就能「十分先进」地将 iPad 的触摸及键入操作反向传回 Mac。
只是这款诞生于 iPad 发售之初的软件远远谈不上「流畅」,甚至有着堪称灾难的使用体验:无论是繁琐的前期设置、极不稳定的连接,还是动辄数秒的延迟,都表明它更像是一次对新技术的探索,而非成熟的解决方案。
所以同年,Avatron Software 推出了 Air Display。它采用了与 iDisplay 相似的思路,进行了一部分的优化,不过仍称不上「好用」,动态画面仍有不小的延迟。此后几年,围绕「iPad 作为扩展副屏」的解决方案不断涌现,但总体而言,彼时「将 iPad 作为扩展副屏」这个点子在大多数人看来还是一种新奇的功能玩法,或是应急需求,没有人真正将它视作生产力的一部分。
读到这相信你也在想:既然「无线」带来了那么多问题,那加上一根线不就行了?巧了,正有一群和你抱有同样想法的前 Apple 工程师。于是 Duet Display 来了。
与上述使用过 Wi-Fi 传输的产品不同,Duet Display 须使用 Lightning 或 30 Pin 数据线连接 Mac 与 iPad。得益于有线这一较为稳定的传输方式,Duet Display 打出了「Retina、60 fps、No Lag」的宣传旗号。
但需要注意的是,这里的「有线连接」有别于普通的显示器连接,它并不能直接将 iPad 扩展为一块普通的显示器 —— 因为iPad 不能通过它直接接收 Mac 输出的视频信号,本质上,它只是将数据传输方式从无线改为有线;画面的生成与显示过程仍与先前的软件类似,需要先在 Mac 中创建一块虚拟显示器,再将画面处理后传送至 iPad。
因此 Duet 也并没有像宣传的那么美好:其 Retina 与 60 fps 模式会占用大量 CPU,部分旧款 Mac 仍会出现光标延迟、画面伪影与系统卡顿。所以再后来,市面上又出现了针对将 iPad 扩展为数位板的 Astropad、以及借助 DisplayPort 硬件适配器、让 Mac 将 iPad 识别为「真实外接显示器」的 Luna Display 等产品......


时至今日,市场上依然活跃着不少第三方随航类软件,其中一些在满足特定需求方面仍有优势,在此暂且按下不表,有兴趣的读者可以根据自己的需求进一步探索。
让我们谈回 Apple。
2019 年 6 月 3 日,Craig Federighi 在 Apple 的 WWDC 2019 上首次公布了一项名为 Sidecar 的新功能,正式将「让 iPad 变成 Mac 副屏」纳入系统功能中。
这里有一段颇有意思的小插曲。随航亮相后,Astropad 作为前文介绍的 Astropad Studio 与 Luna Display 背后的团队,很快在官网发文「抗争」,声称 Apple「高度复制」了自己的产品线,并把这次遭遇称为被 Apple Sherlock1。这番指控究竟有多少事实依据,我们便不得而知了。
虽然 Sidecar 在整场发布会中只出现了不足 60 秒,但其中一些有趣的细节仍值得我们探索。毕竟很长一段时间里,Apple 的软件功能似乎都给人一种印象:它们或许未必最早出现,但被 Apple 整合进系统后,往往能呈现出相较第三方要更加完整的体验。
一帧画面的旅程
简单来说,一帧画面要显示在作为 Mac 副屏的 iPad 上,主要需要经过以下几个环节:
- 虚拟屏幕渲染
- 画面编码
- 数据传输
- 解码显示
前文中提到的 iDisplay、Air Display 等第三方软件,其实已经用上了「虚拟屏幕渲染」的思路。简单来说,它们会先在系统中创建一块虚拟的显示器,并像对待实体显示器一样,为其正常分配桌面空间和分辨率等。此时应用仍然按照正常方式绘制窗口,再由 macOS 中负责管理和合成屏幕画面的 WindowServer 组合成虚拟显示器上的完整画面。
不过这些画面最终不会被送往真正的物理显示接口,而是由系统或第三方软件读取,再进入后续的编码与传输流程。
相比之下,接下来的几个环节的精密协同,才是随航真正与第三方方案拉开差距的地方,也是其能够做到如此流畅的关键。
画面编码
为什么 WindowServer 合成出的画面还不能直接交给 iPad?
传统外接显示器的逻辑其实很简单,GPU 完成一帧画面的渲染与合成后,只需要按照显示器规格生成显示信号,再通过 HDMI、DisplayPort 或 Thunderbolt 等接口送往显示器即可。
但一块高分辨率屏幕每秒产生的数据量远比想象中庞大。做一个并不严谨的计算:以 2388 × 1668 分辨率的画面为例(2018—2022 款 11 英寸 iPad Pro,也是笔者所用的型号),假设每个像素使用 4 字节(RGBA)表示,那么每帧图像所占用的空间大约为 2388 × 1668 × 4 ÷ 1000² ≈ 15.9 MB。如果设备的刷新率为默认的 60Hz,一秒钟产生的原始画面数据便已经接近 1 GB。
对于上述针对外接显示器的有线传输来说,这样的数据量并不是什么大问题。HDMI 2.0 的最高传输带宽可以达到 18 Gbps,而 DisplayPort 1.2 则可以达到 21.6 Gbps,足以承载 4K 60Hz 级别的显示信号。但对于无线传输的随航来说,想要稳定传输如此庞大的原始画面数据显然并不现实。因此在画面离开 Mac 之前,还需要先经过一道关键步骤——编码。
本质上,随航所传输的其实是一条实时视频流,因此它对屏幕画面的压缩也沿用了现代视频 HEVC 编码的基本思路。
HEVC
HEVC 是 High Efficiency Video Coding (高效率视频编码)的缩写,你或许对它的另一个名字更熟悉——H.265。它由 ITU-T 与 MPEG 共同制定,可以看作 H.264/AVC 的继任者。HEVC 的核心精髓便是「猜」,用尽可能少的原始画面,推算出接下来的视频内容,从而节省海量的空间。
那上述的「海量」究竟如何量化?简单做个演示,我们可以借助 FFmpeg 对系统 UI 进行无损录屏。在系统「终端」中输入以下指令,查看 FFmpeg 可以调用的视频设备:
ffmpeg -hide_banner -f avfoundation -list_devices true -i ""
在输出结果中找到 Capture screen 0,并记下它前面的设备编号:

再输入以下指令开启无损录屏(若 Capture screen 0 指令得到的编号不为 3,则在第三行「3:none」中替换为实际的数字):
ffmpeg -f avfoundation -framerate 30 -pixel_format bgr0 -capture_cursor 1 \
-i "3:none" \
-c:v ffv1 -level 3 -coder 1 -context 1 -slicecrc 1 -pix_fmt bgr0 \
"$HOME/Downloads/screen-lossless.mkv"
按 Q 即可停止录制。接下来我们在终端中输入以下命令,将刚刚录制的无损视频压缩为 HEVC:
ffmpeg -i "$HOME/Downloads/screen-lossless.mkv" \
-c:v libx265 -preset medium -crf 23 \
-pix_fmt yuv420p \
"$HOME/Downloads/screen-hevc.mp4"
这种视觉上近乎无损的压缩,几乎节省了 99% 的文件体积:

为了模拟使用随航时画面变化更加频繁的场景(如播放视频),笔者在影视飓风官网的「飓风素材库」中下载了 Bo-Kaap-02 这条素材进行演示。

下图为使用 HEVC 压缩前后的首帧对比截图。相信你和我一样,哪怕是将下图铺满全屏,也很难分辨究竟哪边才是原片。


而经过 HEVC 编码后,原本 363.2 MB 的素材只剩下约 2% 的体积:

为了理解这 98% 的数据究竟去了哪里,我们需要先简单了解 HEVC 的编码原理,也就是笔者前文所说的「猜」的过程。
分块
在预测开始前,HEVC 会先将画面划分为一个个编码树单元,也就是 CTU(Coding Tree Unit)。在 HEVC 中,一个 CTU 最多覆盖 64 × 64 个亮度像素,并可以继续通过四叉树结构划分为尺寸更小的编码单元 CU(Coding Unit)。
这种划分并不是固定的。天空、墙面等颜色和纹理较为平缓的区域,可以保留尺寸较大的 CU;车辆、树木和建筑边缘等细节密集的区域,则往往需要继续向下划分。编码器由此能够根据不同区域的复杂程度,选择更合适的处理粒度。在 StreamEye 中2,这一结构可以被直接观察到。
比如选中画面右侧较为平坦的白墙时,一个 CTU 内只有少量尺寸较大的 CU;切换到细节复杂的塔楼后,分块会明显变得更加密集。
确定 CU 的大小后,编码器还会为其选择预测方式,并根据需要将其划分为一个或多个预测单元,也就是 PU(Prediction Unit)。接下来,编码器会针对这些区域寻找尽可能接近原始画面的预测结果。而用于预测的信息主要有两个来源:一种来自当前画面中已经得到的相邻区域,另一种则来自视频中的其他参考画面。根据参考信源的不同,HEVC 中的预测也就可以分为两大类——「帧内预测」与「帧间预测」。
帧内预测
「帧内预测」(Intra Prediction),顾名思义,即不依赖视频中其它帧,而是利用当前帧中已经得到的相邻画面信息,来预测当前单元中的内容。
这里的「相邻区域」通常位于编码块的上方与左侧。由于画面会按照一定顺序逐块处理,当编码器来到当前块时,这些区域已经完成了编码与重建,因此编码器和解码器都能够获得同一组参考像素。
HEVC 共提供了 35 种亮度帧内预测模式,其中包括 33 种角度模式,以及 Planar(平面)模式与 DC 模式。Planar 模式更适合颜色缓慢变化的区域,它会综合块上方与左侧的参考像素,在块内生成较为平滑的渐变。DC 模式则会根据周围参考像素计算一个平均值,再用这个近似的颜色填充整个块。
你可以点击这个链接查看对各种帧内预测模式的介绍。
回到 StreamEye,这个过程也可以被直观地观察到。这里笔者选择了一帧完全依靠帧内预测完成编码的 I 帧(至于什么是 I 帧,我们将在后文继续说明)。下图左、右两侧分别展示了两种帧内预测模式。左图选中的是天空中颜色过渡较为平缓的区域,编码器采用了 Planar 模式,通过相邻参考像素生成连续、平滑的渐变;右图选中的街景区域则具有更加明显的边缘与纹理走向,因此采用了 25 Angular 模式,沿特定方向将相邻像素的信息延伸至当前块内。


通过这些不同的预测模式,编码器能够利用单张画面内部的相似性,减少重复记录的像素信息。不过,帧内预测的参考范围始终局限于当前画面,而视频的前后画面之间往往也存在大量相似内容,于是便有了另一种预测方式——帧间预测。
帧间预测
与只参考当前画面的帧内预测不同,帧间预测(Inter Prediction)会从其他已经重建的参考画面中(包括前、后相邻帧),寻找与当前编码块相似的区域。
视频中的物体通常不会在相邻两帧之间发生剧烈变化。同样的,对于随航来说,桌面画面也包含大量的重复。菜单栏、工具栏等 UI 界面也不会发生大范围变化。
于是,当编码器拿到当前单元后,会尝试在参考画面中寻找一块与其尽可能相似的区域。这个搜索过程被称为「运动估计」。参考区域与当前单元之间的位移,被称为「运动矢量」。最后,编码器会记录相应的信息并传递,解码器则利用该信息还原预测画面,该过程被称为「运动补偿」。
需要注意的是,这里的「运动」与我们通常理解的物体「运动」并不是一回事。它记录的是当前块与参考画面中相似区域之间的位移方向与距离。在 StreamEye 中,打开运动矢量的可视化显示。画面上便会出现各个预测块所使用的位移信息。在细节较多、画面复杂的预测块中,运动向量往往分布密集且方向不一:

而在墙面等相对稳定的区域,相邻块的运动矢量则具有相近的方向,且分布较少:

残差
在 StreamEye 中切换至「Predicted」模式,便可以直接看到编码器预测出的画面。不难发现,画面左下角细节较为复杂的区域出现了明显的块状结构,几乎已经糊成了一团马赛克。

这样的预测结果显然无法直接使用。这时,编码器便会比对预测画面与视频中对应的原始帧,并计算两者之间的差异。所得到的差值,便被称为「残差」(Residual)。
在 StreamEye 中切换至「Residual」模式,也可以直接观察到残差的可视化结果。仔细观看会发现,画面中出现较为明显的残差主要集中于物体边缘轮廓这类过渡剧烈的区域,而其他部分几乎没有留下多少信息。此时预测结果已经足够接近原始画面,编码器只需再记录少量差异信息即可。

残差还需要依次经过变换、量化和熵编码,最终与预测模式、运动矢量等信息一起写入码流。解码时,这套流程则反向进行,解码器先通过熵解码、反量化和反变换重建残差,再利用帧内预测模式或运动矢量生成预测画面,最后将两者相加,得到重建画面。
其中变换、量化以及熵编码等环节的处理过程并没有那么直观,且涉及较多数学原理,这里便不再展开。
I、P 与 B 帧
前文中我们提到了「I 帧」,而随它一同出现的,还有 P 帧与 B 帧。
它们是根据画面所采用的预测方式,对视频帧进行的三种分类,其区别主要在于编码时能够参考哪些信息。
- I 帧(Intra-coded Picture)只采用帧内预测。它不需要参考视频中的其他画面,仅依靠自身已经重建的相邻区域便能完成解码。因此,I 帧通常会成为一段视频的起点,需要记录的数据通常最多,体积也相对较大。
- P 帧(Predictive-coded Picture)则可以参考已经解码的画面进行帧间预测。以前文缓慢推进的街景为例,P 帧从参考画面中找到相似区域,再通过运动矢量描述它们的位置变化,最后使用残差补充无法准确预测的部分。
- B 帧(Bi-predictive Picture)拥有更加灵活的参考方式。它可以从两组参考画面中寻找合适的内容,而这些参考画面可能分别位于当前画面的前方与后方。简单地说,B 帧是一种双向预测,而 P 帧则是单向预测。
I、P 与 B 帧按照一定方式排列,便构成了 GOP(Group of Pictures,图像组)。一个 GOP 通常从能够作为解码起点的画面开始,随后通过 P 帧与 B 帧不断引用已经重建的内容,直到下一组画面开始。
前文的原始视频中,每一帧都是 I 帧,这也是其占用存储空间较大的原因。