WWDC Quick Look 💓 By SwiftGGTeam
What's new in camera capture

What's new in camera capture

观看原视频

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 支持此功能

核心启发

  1. 智能扫码助手

    • 做什么:扫码 App 自动检测 QR 码大小,距离太近时自动变焦并提示用户后退
    • 为什么值得做minimumFocusDistance 让扫码体验从”凑近试试”变成”自动适配”
    • 怎么开始:根据码尺寸和视场角计算最小距离,与 minimumFocusDistance 比较,自动设置 videoZoomFactor
  2. 专业视频录制工具

    • 做什么:相机 App 支持 10-bit HDR 视频录制,直接输出 Dolby Vision 兼容文件
    • 为什么值得做:Apple 自动处理 Dolby Vision 元数据,开发者只需要选择 x420 格式
    • 怎么开始:遍历 device.formats 找到 x420 格式设为 activeFormat,用 AVCaptureMovieFileOutputAVAssetWriter 录制
  3. 视频会议效果控制面板

    • 做什么:在 App 内提供 Center Stage 和 Portrait 模式的开关,与 Control Center 状态同步
    • 为什么值得做:cooperative 模式让用户既能在系统层面控制,也能在 App 内快速切换
    • 怎么开始:设置 centerStageControlMode = .cooperative,监听 isCenterStageEnabled 变化,在 UI 中反映当前状态
  4. 多机位直播推流

    • 做什么:同时使用前后摄像头进行画中画直播,利用 IOSurface 压缩降低内存占用
    • 为什么值得做:多机位 session 的内存带宽压力很大,压缩后的 surface 在 Metal pipeline 中零拷贝处理
    • 怎么开始:配置 AVCaptureMultiCamSession,VideoDataOutput 使用压缩像素格式,Metal shader 直接读取压缩 surface 进行合成
  5. 自适应帧率相机

    • 做什么:相机 App 根据系统压力状态动态调整帧率,避免过热和丢帧
    • 为什么值得做systemPressureState 提供了 nominal/fair/serious/critical/shutdown 五级状态,可以提前采取措施
    • 怎么开始:监听 systemPressureState 变化,serious 级别时降低 activeMinVideoFrameDuration,critical 时关闭非必要功能

关联 Session

评论

GitHub Issues · utterances