Highlight
iOS 26 引入 Deferred Start API,允许相机 App 将非预览输出(如照片、视频输出)的初始化推迟到首帧预览渲染之后,配合 Responsive Capture 可实现启动时间减半,同时不丢失拍摄时机。
核心内容
相机启动慢,用户最直观的感受是什么
你打开相机 App,屏幕却是一片黑。等了几秒,预览画面终于出现,但想拍的那个瞬间已经过去了。演讲者用一个多米诺骨牌的例子说明了这个问题:红色骨牌即将倒下时打开相机,如果启动太慢,预览出现时骨牌早已翻倒。
这个痛点背后有四个阶段:App 启动加载、Session 配置与启动、输出对象初始化、预览帧开始流动。其中第三阶段——初始化 AVCaptureOutput——是最耗时的。
Deferred Start:把重活放到预览之后
以前的做法是所有输出在 startRunning() 时一并初始化。AVCapturePhotoOutput 和 AVCaptureMovieFileOutput 这类重量级对象会拖慢整个启动流程,但它们对预览本身没有贡献。
Deferred Start 的核心思路很简单:只让预览输出先初始化,其他输出延后。每个 AVCaptureOutput 和 AVCaptureVideoPreviewLayer 新增了 isDeferredStartEnabled 属性,设为 true 即可将该输出的初始化推迟。
(02:05)演讲者展示了实验室对比:未启用 Deferred Start 时启动耗时接近 1 秒,启用后缩短到约 0.5 秒,启动速度翻倍。配置越复杂的 Session,收益越大。
预览出来了,但快门按不下去怎么办
Deferred Start 有个副作用:预览虽然快了,但照片输出还没初始化完,用户点快门没有反应。这在抓拍场景下是致命的。
Apple 的解决方案是 Responsive Capture。开启后,AVCapturePhotoOutput 会在内部增加缓冲,允许在输出完全初始化前就开始捕获。演讲者再次用多米诺骨牌演示:开启 Responsive Capture 的绿色手机成功抓拍到了红色骨牌倒下的瞬间,未开启的紫色手机则错过了。
(14:07)
持续性能:别让用户觉得相机”卡”
预览开始流动后,保持稳定的帧率同样重要。设备发热时系统会降频,如果 App 不做适配,预览就会掉帧。
AVCaptureSession 新增了 hardwareCost 属性(取值 0 到 1),表示当前配置占用的硬件资源比例。超过 1 表示配置超出了设备能力,需要在启动前降级。AVCaptureDevice 的 systemPressureState 则用于运行时监听系统压力,压力升高时应降低帧率或减少 GPU/ANE 使用。
(20:16)
ProRes 录制的 I/O 难题
高码率视频(如 ProRes)录制对磁盘写入速度要求极高。传统文件系统的 I/O 行为是不确定的——后台索引、内存碎片、存储磨损都会影响写入速度,导致掉帧。
iOS 27 引入 AVProVideoStorage,通过预分配存储空间来提供确定性的写入性能。它是个系统级单例,所有 App 共享。录制前检查 remainingCapacity 和 isBusy,然后设置 usesProVideoStorage = true 即可。
(22:17)
详细内容
自动 Deferred Start
用 AVCaptureVideoPreviewLayer 渲染预览的 App,升级到 iOS 26 SDK 重新编译后默认启用自动模式。系统会在预览出现后不久自动完成剩余输出的初始化。
import AVFoundation
class DeferredStartDelegate: NSObject, AVCaptureSessionDeferredStartDelegate {
func sessionWillRunDeferredStart(_ session: AVCaptureSession) {
// 延迟初始化开始前调用,可在此创建后台资源
}
func sessionDidRunDeferredStart(_ session: AVCaptureSession) {
// 延迟初始化完成后调用,所有输出已就绪
}
}
let captureSession = AVCaptureSession()
captureSession.beginConfiguration()
captureSession.automaticallyRunsDeferredStart = true
let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer.isDeferredStartEnabled = false
let photoOutput = AVCapturePhotoOutput()
photoOutput.isDeferredStartEnabled = true
captureSession.addOutput(photoOutput)
captureSession.setDeferredStartDelegate(
deferredStartDelegate,
deferredStartDelegateCallbackQueue: sessionQueue
)
captureSession.commitConfiguration()
captureSession.startRunning()
关键点:
automaticallyRunsDeferredStart在 iOS 26 SDK 下默认truevideoPreviewLayer.isDeferredStartEnabled = false确保预览优先初始化photoOutput.isDeferredStartEnabled = true将照片输出推迟setDeferredStartDelegate注册回调,用于感知延迟启动的进度sessionQueue应使用专门的串行队列,避免阻塞主线程
(09:14)
手动 Deferred Start
使用 AVCaptureVideoDataOutput 自定义预览渲染的 App,需要采用手动模式。通过 CAMetalLayer 的 addPresentedHandler 捕获首帧上屏时机,再触发延迟初始化。
import AVFoundation
import QuartzCore
let captureSession = AVCaptureSession()
captureSession.beginConfiguration()
captureSession.automaticallyRunsDeferredStart = false
let videoOutput = AVCaptureVideoDataOutput()
captureSession.addOutput(videoOutput)
videoOutput.isDeferredStartEnabled = false
let photoOutput = AVCapturePhotoOutput()
photoOutput.isDeferredStartEnabled = true
captureSession.addOutput(photoOutput)
captureSession.setDeferredStartDelegate(
deferredStartDelegate,
deferredStartDelegateCallbackQueue: sessionQueue
)
captureSession.commitConfiguration()
captureSession.startRunning()
// 在 Metal 渲染循环中
private var firstFramePresented = false
guard let drawable = layer.nextDrawable() else { return }
if !firstFramePresented {
drawable.addPresentedHandler { _ in
captureSession.runDeferredStartWhenNeeded()
}
firstFramePresented = true
}
关键点:
automaticallyRunsDeferredStart = false关闭自动模式videoOutput.isDeferredStartEnabled = false确保自定义预览先就绪addPresentedHandler在首帧呈现后回调runDeferredStartWhenNeeded()通知系统可以开始延迟初始化- 手动模式适合需要精确控制启动流程的 App
(11:30)
启用 Responsive Capture
Responsive Capture 在 AVCapturePhotoOutput 上增加了一个缓冲层,允许在输出完全初始化前就开始捕获处理。
import AVFoundation
func configurePhotoOutput(for session: AVCaptureSession, device: AVCaptureDevice) {
let photoOutput = AVCapturePhotoOutput()
guard session.canAddOutput(photoOutput) else { return }
session.addOutput(photoOutput)
photoOutput.maxPhotoQualityPrioritization = .quality
photoOutput.isResponsiveCaptureEnabled = photoOutput.isResponsiveCaptureSupported
}
关键点:
isResponsiveCaptureSupported先检查设备是否支持isResponsiveCaptureEnabled = true开启响应式捕获- 与 Deferred Start 配合使用效果最好
- 以略微增加处理延迟为代价,换取快门响应速度
(14:07)
监控系统压力
import AVFoundation
let captureSession = AVCaptureSession()
let device = activeVideoInput?.device
captureSession.beginConfiguration()
// ... 配置 session ...
captureSession.commitConfiguration()
guard captureSession.hardwareCost <= 1.0 else {
print("hardwareCost \(captureSession.hardwareCost) — cannot start session. Reconfiguring.")
setupLowCostConfiguration()
}
captureSession.startRunning()
let systemPressureObserver = device?.observe(
\.systemPressureState,
options: [.initial, .new],
changeHandler: { device, change in
// 根据压力状态调整帧率或降低 GPU/ANE 使用
}
)
关键点:
hardwareCost在commitConfiguration()后检查,超过 1.0 需降级配置systemPressureState通过 KVO 监听,实时响应系统压力变化- 压力升高时可降低帧率、使用 binned format 或减少 UI 工作量
(20:16)
使用 AVProVideoStorage
import AVFoundation
func configureProVideoStorage() {
guard AVProVideoStorage.isSupported else { return }
let storage = AVProVideoStorage.shared
guard storage.remainingCapacity != 0 else {
storage.openSettings()
return
}
}
// 录制配置
guard AVProVideoStorage.isSupported else { return }
guard let pvs = AVProVideoStorage.shared else { return }
let movieOutput = AVCaptureMovieFileOutput()
guard movieOutput.isProVideoStorageSupported else { return }
guard !pvs.isBusy else { return }
let movieFileURL = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("mov")
movieOutput.usesProVideoStorage = true
movieOutput.startRecording(to: movieFileURL, recordingDelegate: delegate)
关键点:
AVProVideoStorage.isSupported检查设备是否支持remainingCapacity检查剩余预分配空间,为 0 时引导用户到设置isBusy检查存储是否正被其他任务占用isProVideoStorageSupported确认输出支持该特性usesProVideoStorage = true开启后,录制文件先写入预分配空间,结束后再移到目标位置AVAssetWriter也支持usesProVideoStorage属性
(22:17)
核心启发
-
做一款”秒开”的社交相机 App:用 Deferred Start 让预览在 0.5 秒内出现,配合 Responsive Capture 确保用户打开 App 瞬间就能抓拍。实现思路:自动模式下只保留预览层,照片输出延迟初始化,快门按钮在
sessionDidRunDeferredStart回调后才变为完全可用状态。 -
为直播 App 添加发热降频保护:通过监听
systemPressureState,在设备过热时自动降低采集分辨率或帧率,避免直播画面卡顿或 App 被系统强制终止。实现思路:在changeHandler中根据压力等级切换activeFormat,使用frameRateOverride精确控制帧率。 -
在 iPhone 上实现 ProRes 专业录制:利用
AVProVideoStorage消除高码率录制的 I/O 抖动,为视频创作者提供稳定的专业录制体验。实现思路:录制前检查remainingCapacity和isBusy,设置usesProVideoStorage = true,录制结束后从临时目录移出文件。 -
用 VideoDataOutput 做实时滤镜预览:自定义 Metal 渲染管线,在
AVCaptureVideoDataOutput的captureOutput(_:didOutput:from:)中对每帧应用滤镜后再呈现。实现思路:采用手动 Deferred Start,首帧上屏后调用runDeferredStartWhenNeeded(),同时把照片输出延迟初始化。 -
在相机启动时做 UI 分级加载:第一阶段只显示预览和快门按钮,第二阶段在
sessionDidRunDeferredStart后淡入相册缩略图、模式选择器等非关键 UI。实现思路:将 UI 创建逻辑拆分到两个方法中,通过 Deferred Start 的 delegate 回调触发第二阶段。
关联 Session
- Implement high-resolution photo capture — 深入讲解 Responsive Capture 与高质量照片拍摄的结合
- Create a more responsive camera experience — Center Stage 和相机响应性优化
- Metal performance optimization — 自定义预览渲染时的 GPU 性能调优
- Xcode 27 — 使用 Instruments 测量相机启动性能
- Instruments — 定位相机 App 中的性能瓶颈
评论
GitHub Issues · utterances