Create camera extensions with Core Media IO
Media & Web 进阶 20m

用 Core Media IO 创建摄像头扩展:安全、现代、Swift 原生

Create camera extensions with Core Media IO

2022年6月6日

在 Apple 官方观看视频

一句话判断

DAL plug-in 时代正式落幕——Camera Extensions 是 macOS 摄像头驱动的唯一未来,如果你还在维护 DAL plug-in,现在是时候动手迁移了,因为 Apple 明确说下一个大版本就会彻底禁用 DAL。

这场 Session 讲了什么

macOS 从 10.7 开始支持的 DAL plug-in 是一套古老的摄像头驱动架构。它最大的问题是把第三方代码直接加载到 app 进程里,这让 FaceTime、QuickTime Player 等 Apple 自家应用以及大量第三方相机应用都不敢用它——谁愿意让一个可能有 bug 的插件把自己的 app 搞崩?而且 DAL plug-in 用的是 C/C++ API,文档稀少,开发体验很糟糕。

macOS 12.3 引入了全新的 Camera Extensions,基于 SystemExtensions 框架。你的扩展代码跑在独立的沙盒 daemon 进程里,所有 buffer 在传递给 app 前都会被验证。它可以有 Swift 和 Objective-C 两种实现,API 简洁到只有几个核心类。最重要的是,Camera Extensions 在所有相机 app 里都能显示——包括 FaceTime。

Session 展示了三种典型用法:纯软件摄像头(测试用)、硬件摄像头驱动(通过 DriverKit 或 legacy kext)、以及创意摄像头(从物理摄像头获取画面,叠加滤镜后作为新的摄像头流输出)。演示的 CIFilterCam 例子很直观:一个配置 app 控制滤镜参数,所有使用 CIFilterCam 的 app 实时看到滤镜效果变化。

值得深挖的点

沙盒模型是亮点也是约束。 Camera Extensions 跑在一个叫 _cmiodalassistants 的 role user 下,有定制的沙盒 profile。你可以访问 USB、Bluetooth、WiFi 作为客户端,读写自己的 container 和 tmp。但是你不能 fork、exec、访问 WindowServer、注册全局 mach service。如果你发现沙盒限制太严格,Apple 让你通过 Feedback Assistant 反馈。我的判断是,对于绝大多数摄像头场景这个沙盒够用,但如果你需要做一些特殊的事情(比如网络摄像头),可能需要提前和 Apple 沟通。

DAL plug-in 的死刑宣判。 Apple 明确表示在 macOS Ventura 之后的下一个大版本会完全禁用 DAL plug-in。这不是建议,是时间线。如果你有现存的 DAL plug-in 用户,你需要现在就开始迁移计划。好在新旧系统的设备 ID 兼容性做得不错——你可以通过 legacyDeviceID 保持和之前 uniqueIdentifier 的兼容。

代码片段

创建并启动 Camera Extension 服务:

// ExtensionProvider.swift — 扩展的入口点
// main.swift 会调用 startService
let providerSource = MyProviderSource()
let provider = ExtensionProvider(source: providerSource)
ExtensionProvider.startService(provider: provider)

// 注意:Info.plist 中必须包含 CMIOExtensionMachServiceName
// 否则 registerassistant 无法启动你的扩展

用 custom properties 控制创意摄像头:

// 自定义属性通过 4cc_ 前缀桥接到 CoreMedia IO 的 C 属性接口
// 格式:4cc_[selector]_[scope]_[element]
// 例如:"4cc_filt_glob_0" 对应 selector="filt", scope=global, element=0

// 在配置 app 中设置属性
extensionDevice.setPropertyState(
    value: selectedFilterName,
    forProperty: "4cc_filt_glob_0"
)

// 在扩展代码中读取属性,应用对应滤镜
// AVFoundation 不支持自定义属性,必须用 CoreMedia IO C API

Hardware camera 权限配置:

<!-- Entitlements: 需要使用其他摄像头时 -->
<key>com.apple.security.device.camera</key>
<true/>

<!-- Info.plist: 必须提供使用说明 -->
<key>NSCameraUsageDescription</key>
<string>CIFilterCam 需要访问摄像头以应用滤镜效果</string>

最佳实践

  • 把 Camera Extension 打包在 app bundle 里通过 App Store 分发,利用 SystemExtensions 框架处理安装、升级、卸载。用户只需删除 app 就能移除扩展。
  • 开发时注意:app 必须在 /Applications 目录下才能安装 system extension,直接从 Xcode 运行会失败。
  • 使用 overlayContainerView 来添加自定义高亮视图,这样它们会出现在 camera preview 之上、其他 UI 元素之下。
  • AVFoundation 会忽略除第一个 input stream 之外的所有 stream,如果你需要多个输出,要考虑其他方案。
  • 对于 creative camera,macOS Ventura 才支持在 extension 内访问其他摄像头。

还有什么值得关注

  • Camera Extensions 可以作为 output device 消费视频流(用于 print-to-tape 或实时预览),但这需要直接用 CoreMedia IO C API,AVFoundation 不支持。
  • 注册的代理服务 registerassistantservice 负责透明度、同意和控制策略,包括用户授权和功耗归因。
  • Apple 为 UVC(USB Video Class)摄像头提供了 class compliant extension,只有非标准协议的 USB 摄像头才需要自定义 extension。
WWDC 2022