Highlight
iOS 15 为相机捕获带来五项升级:最小对焦距离报告、10-bit HDR 视频、Control Center 视频效果(Center Stage / Portrait / Mic Modes)、性能优化最佳实践、以及 IOSurface 压缩。
核心内容
扫描一个 20mm 的 QR 码,用户把手机凑得很近,结果码变模糊了。这是因为手机镜头的最小对焦距离有限,凑太近反而对不上焦。iOS 15 之前,开发者不知道这个距离是多少,只能靠猜。现在 AVCaptureDevice 公开了 minimumFocusDistance 属性,App 可以据此自动计算需要的变焦倍数,引导用户后退到合适距离。
(02:17)另一个重磅功能是 10-bit HDR 视频。iPhone 12 开始支持,它使用 x420 像素格式(10-bit YUV),配合 HLG 曲线和 BT.2020 色域,同时内置 EDR 高光恢复。更关键的是,Apple 自动在每帧中插入 Dolby Vision 元数据,录制的视频直接兼容 Dolby Vision 显示设备。
(12:14)本场的主角是 Control Center 视频效果。Center Stage、Portrait 模式和 Mic 模式是系统级功能,用户打开后,所有视频通话 App 自动生效,不需要开发者写任何代码。但 Apple 也提供了 API,让 App 可以检测状态、自定义 UI,甚至与应用内的其他功能协同。
Center Stage 利用 M1 iPad Pro 的 1200 万像素超广角前摄,自动追踪画面中的人物,始终保持构图。Portrait 模式用 Neural Engine 生成单目深度图,模拟大光圈镜头的浅景深效果。Mic 模式有三种:标准、语音隔离(降噪)和宽频谱(保留环境音)。
(30:43)最后,iOS 15 引入了 IOSurface 压缩,一种无损的内存视频压缩格式。支持 iPhone 12 系列、2020 秋季 iPad Air 和 M1 iPad Pro。如果 App 使用 Metal、Core Image 等硬件加速 pipeline,开启压缩后可以显著降低内存带宽占用。
详细内容
自动计算 QR 码扫描距离
let deviceFieldOfView = self.videoDeviceInput.device.activeFormat.videoFieldOfView
let minSubjectDistance = minSubjectDistanceForCode(
fieldOfView: deviceFieldOfView,
minimumCodeSize: 20,
previewFillPercentage: Float(rectOfInterestWidth)
)
private func minSubjectDistance(
fieldOfView: Float,
minimumCodeSize: Float,
previewFillPercentage: Float
) -> Float {
let radians = degreesToRadians(fieldOfView / 2)
let filledCodeSize = minimumCodeSize / previewFillPercentage
return filledCodeSize / tan(radians)
}
关键点:
videoFieldOfView是相机当前格式的水平视场角minimumCodeSize是要扫描的最小码尺寸,单位毫米previewFillPercentage是码在预览画面中应占的宽度比例- 计算出的
minSubjectDistance是能让码清晰对焦的最小距离
距离太近时自动变焦
let deviceMinimumFocusDistance = Float(self.videoDeviceInput.device.minimumFocusDistance)
if minimumSubjectDistanceForCode < deviceMinimumFocusDistance {
let zoomFactor = deviceMinimumFocusDistance / minimumSubjectDistanceForCode
do {
try videoDeviceInput.device.lockForConfiguration()
videoDeviceInput.device.videoZoomFactor = CGFloat(zoomFactor)
videoDeviceInput.device.unlockForConfiguration()
} catch {
print("Could not lock for configuration: \(error)")
}
}
关键点:
minimumFocusDistance是 iOS 15 新属性,单位米- 如果目标距离小于最小对焦距离,计算变焦倍数让用户后退
- 必须先
lockForConfiguration()才能修改videoZoomFactor - iPhone 12 Pro Max 广角镜头的最小对焦距离是 15cm,iPhone 12 Pro 是 12cm
启用 10-bit HDR 视频格式
func firstTenBitFormatOfDevice(device: AVCaptureDevice) -> AVCaptureDevice.Format? {
for format in device.formats {
let pixelFormat = CMFormatDescriptionGetMediaSubType(format.formatDescription)
if pixelFormat == kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange /* 'x420' */ {
return format
}
}
return nil
}
// 设置为活动格式
if let tenBitFormat = firstTenBitFormatOfDevice(device: videoDevice) {
do {
try videoDevice.lockForConfiguration()
videoDevice.activeFormat = tenBitFormat
videoDevice.unlockForConfiguration()
} catch {
print("Could not set format: \(error)")
}
}
关键点:
- x420 格式是 10-bit biplanar YUV,视频范围(16-235)
- iPhone 12 的格式列表中,每个分辨率和帧率对应三个格式:420v、420f、x420
- 10-bit HDR 视频支持 720p、1080p、4K 和 1920x1440(4:3)
- Apple 自动插入 Dolby Vision 元数据,兼容 Dolby Vision 显示设备
检测 Control Center 视频效果状态
// Center Stage
let isCenterStageEnabled = AVCaptureDevice.isCenterStageEnabled
let isCenterStageActive = videoDevice.isCenterStageActive
// Portrait
let isPortraitActive = videoDevice.isPortraitEffectActive
// Mic Mode
let preferredMicMode = AVCaptureDevice.preferredMicrophoneMode
let activeMicMode = AVCaptureDevice.activeMicrophoneMode
关键点:
isCenterStageEnabled是类属性,反映 Control Center 中该 App 的开关状态isCenterStageActive是实例属性,表示当前相机是否正在应用 Center Stage- Center Stage 限制:最高 30fps,最大输出分辨率 1920x1440,变焦锁定为 1x
- Portrait 模式限制:最高 30fps,最大分辨率 1920x1440,仅前摄
以 Cooperative 模式控制 Center Stage
// 设置为 cooperative 模式,App 和 Control Center 都能控制
do {
try videoDevice.lockForConfiguration()
videoDevice.centerStageControlMode = .cooperative
videoDevice.isCenterStageEnabled = true
videoDevice.unlockForConfiguration()
} catch {
print("Could not configure Center Stage: \(error)")
}
// 监听状态变化,保持 UI 同步
videoDevice.addObserver(self, forKeyPath: "isCenterStageEnabled", options: .new, context: nil)
关键点:
.user模式(默认):只有用户能在 Control Center 切换,App 尝试修改会抛异常.app模式:只有 App 能控制,Control Center 变灰,不建议使用.cooperative模式:App 和 Control Center 都能控制,需要监听状态保持同步- FaceTime 是 cooperative 模式的典型用例
处理丢帧
func captureOutput(
_ output: AVCaptureOutput,
didDrop sampleBuffer: CMSampleBuffer,
from connection: AVCaptureConnection
) {
guard let attachment = sampleBuffer.attachments[.droppedFrameReason],
let reason = attachment.value as? String else { return }
switch reason as CFString {
case kCMSampleBufferDroppedFrameReason_FrameWasLate:
// 处理太慢,考虑降低帧率或减少工作量
reduceFrameRate()
case kCMSampleBufferDroppedFrameReason_OutOfBuffer:
// 缓冲区不足,检查是否持有过多 buffer
releaseHeldBuffers()
case kCMSampleBufferDroppedFrameReason_Discontinuity:
// 系统级问题,非 App 原因
break
default:
fatalError("A frame dropped for an undefined reason.")
}
}
关键点:
alwaysDiscardsLateVideoFrames默认为 true,保留最新帧丢弃旧帧- 如果需要录制所有帧(如 AVAssetWriter),应设为 false 并自己保证实时性
FrameWasLate是最常见原因,动态降低activeMinVideoFrameDuration可以缓解OutOfBuffer说明 App 持有太多 buffer,应及时释放
启用 IOSurface 压缩
let videoDataOutput = AVCaptureVideoDataOutput()
// 检查是否支持压缩 BGRA
let compressedFormat = kCVPixelFormatType_Lossless_420YpCbCr10BiPlanarVideoRange
if videoDataOutput.availableVideoPixelFormatTypes.contains(compressedFormat) {
videoDataOutput.videoSettings = [
kCVPixelBufferPixelFormatTypeKey as String: compressedFormat
]
} else {
// 回退到未压缩格式
videoDataOutput.videoSettings = [
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA
]
}
关键点:
- IOSurface 压缩是无损的,支持 420v、420f、x420 和 BGRA 的压缩变体
- 仅当数据完全在硬件 pipeline 中处理时才有效(Metal、Core Image、AVAssetWriter)
- 不要用 CPU 读写压缩 surface,物理内存布局不透明且可能变化
- iPhone 12 系列、2020 iPad Air、M1 iPad Pro 支持此功能
核心启发
-
智能扫码助手
- 做什么:扫码 App 自动检测 QR 码大小,距离太近时自动变焦并提示用户后退
- 为什么值得做:
minimumFocusDistance让扫码体验从”凑近试试”变成”自动适配” - 怎么开始:根据码尺寸和视场角计算最小距离,与
minimumFocusDistance比较,自动设置videoZoomFactor
-
专业视频录制工具
- 做什么:相机 App 支持 10-bit HDR 视频录制,直接输出 Dolby Vision 兼容文件
- 为什么值得做:Apple 自动处理 Dolby Vision 元数据,开发者只需要选择 x420 格式
- 怎么开始:遍历
device.formats找到 x420 格式设为 activeFormat,用AVCaptureMovieFileOutput或AVAssetWriter录制
-
视频会议效果控制面板
- 做什么:在 App 内提供 Center Stage 和 Portrait 模式的开关,与 Control Center 状态同步
- 为什么值得做:cooperative 模式让用户既能在系统层面控制,也能在 App 内快速切换
- 怎么开始:设置
centerStageControlMode = .cooperative,监听isCenterStageEnabled变化,在 UI 中反映当前状态
-
多机位直播推流
- 做什么:同时使用前后摄像头进行画中画直播,利用 IOSurface 压缩降低内存占用
- 为什么值得做:多机位 session 的内存带宽压力很大,压缩后的 surface 在 Metal pipeline 中零拷贝处理
- 怎么开始:配置
AVCaptureMultiCamSession,VideoDataOutput 使用压缩像素格式,Metal shader 直接读取压缩 surface 进行合成
-
自适应帧率相机
- 做什么:相机 App 根据系统压力状态动态调整帧率,避免过热和丢帧
- 为什么值得做:
systemPressureState提供了 nominal/fair/serious/critical/shutdown 五级状态,可以提前采取措施 - 怎么开始:监听
systemPressureState变化,serious 级别时降低activeMinVideoFrameDuration,critical 时关闭非必要功能
关联 Session
- Capture high-quality photos using video formats — 不修改代码就能提升静态照片质量
- Edit and play back HDR video with AVFoundation — HDR 视频的编辑、播放和色调映射
- Improve access to Photos in your app — PHPicker 改进和 PhotoKit 跨设备同步
评论
GitHub Issues · utterances