Skip to main content

Chapter 172: Apple Deep Path Camera Request

本章追踪一个 Apple 平台上的相机请求:应用打开预览、拍照、编码结果、保存到相册。读完后,读者应能把一次 AVCaptureSession 调用定位到 App、AVFoundation、Core Media、隐私授权、系统媒体服务、驱动与硬件、媒体保存这几层,并能判断一次失败属于权限、会话配置、设备占用、媒体输出、编码保存,还是私有硬件路径的不可见边界。

贯穿材料是一条最小但完整的路径:用户点击“拍照”,App 请求 camera 权限,配置 session graph,启动 preview,触发 photo capture,把结果写入文件或 Photos。这个路径的价值在于它同时经过用户授权、资源仲裁、实时 buffer 流、ISP 处理和媒体持久化,足够暴露移动 OS 对硬件能力的封装方式。

Apple 相机链路的公开 API 主要在 AVFoundationCore MediaCore VideoPhotos 中。公开材料能确认 App 使用的对象、回调、buffer 类型和隐私入口;系统媒体 daemon、ISP firmware、sensor 控制协议和私有驱动队列属于 Apple 封闭实现。本章会把公开事实、可观察行为和合理推断分开讲,防止把私有内部路径写成已公开源码。

最终结论是:Apple camera request 不是一条 App 直接控制 camera sensor 的通道,而是一条被 framework 和系统服务代理的能力请求链。App 描述想要的 session graph,系统检查隐私授权和资源状态,媒体服务把请求映射到设备、ISP 和 buffer 流,AVFoundation 再把结果包装成 preview、photo、video 或 sample buffer 回调。

172.1 Camera Request 的 Apple 跨层路径总览

Camera request 的主线可以从 App 可见行为开始定位。用户看到的是预览画面和拍照按钮,App 代码看到的是 AVCaptureSessionAVCaptureDeviceInputAVCapturePhotoOutputAVCaptureVideoDataOutput,系统内部看到的是对摄像头、麦克风、ISP、display、encoder、Photos library 的多资源协调。相机路径因此天然跨过应用、framework、隐私、系统服务、驱动硬件和媒体保存几层。

一次拍照请求的最小路径如下图。图中的 Service / Daemon 是责任边界名称,具体进程名和内部队列在 iOS 上属于私有实现;图的目的在于固定请求如何跨层传播,以及每一层负责什么判断。

这条路径有两个方向。请求方向从 App 的意图开始:选择 camera device,声明 input 和 output,设置 preset,启动 session,触发 capture。返回方向从硬件产生的 frame 开始:sensor 曝光,ISP 处理,buffer 附带 timestamp 和 metadata,media service 把结果送回 AVFoundation,App 通过 delegate 或输出回调接收。

路径中的第一类边界是权限边界。Camera 是隐私敏感硬件,App 需要在 Info.plist 中提供 NSCameraUsageDescription,并通过 AVCaptureDevice.requestAccess(for:completionHandler:) 或等价入口触发用户授权。授权结果会影响后续 session 启动、设备打开和错误返回。

路径中的第二类边界是资源边界。摄像头通常由系统独占或按优先级仲裁,多个 session、系统相机、视频通话、屏幕录制、Face ID 相关硬件使用、低电量或热状态都可能改变可用结果。App 看到的不是 driver 错误码,而是设备不可用、session interrupted、runtime error、output delegate 停止回调、capture 失败等 framework 表现。

路径中的第三类边界是媒体边界。Preview 追求低延迟,photo capture 追求画质和 metadata,video capture 追求持续编码和音画同步,sample buffer 输出追求 App 可处理的帧流。它们共享相机硬件和部分 ISP 资源,但对 buffer、队列、时间戳、编码器和存储路径提出不同要求。

读 Apple camera request 时,稳定的判断顺序是:先看 App 是否具备权限和 usage description,再看 session graph 是否能成立,再看设备是否被系统保留,再看输出类型和 buffer 回调是否符合预期,最后再讨论 ISP、sensor 和 driver 这类私有硬件路径。这个顺序能把公开可验证的问题放在前面,把封闭实现放在边界内处理。

172.2 App Layer:AVCaptureSession、AVCaptureDevice、AVCaptureOutput

App 层的核心任务是描述相机使用意图。AVCaptureSession 代表一张 capture graph:它连接输入设备、输出对象、连接关系和配置参数;AVCaptureDevice 代表可选择的硬件能力入口;AVCaptureOutput 代表 App 想接收的结果类型。Apple 的 Setting Up a Capture Session 把 session、input、output 和 start 的关系作为公开编程模型。

Session graph 不是单个函数调用。App 先选择设备,例如后置广角 camera;再把设备包装成 AVCaptureDeviceInput;再添加 photo、video、metadata 或 file output;再提交 configuration;最后在合适队列上启动 startRunning()。这些动作共同形成一个系统可审查的声明:App 想从哪类设备取数据,以什么质量、什么格式、什么回调路径接收。

下面的简化 Swift 代码只展示责任边界。它说明 App 层能控制的是 graph 描述和公开配置,系统层负责权限检查、设备打开、buffer 调度和硬件访问。

import AVFoundation

enum CameraSetupError: Error {
case denied
case missingDevice
case invalidGraph
}

final class PhotoCamera: NSObject {
private let session = AVCaptureSession()
private let photoOutput = AVCapturePhotoOutput()

func configure(completion: @escaping (Result<Void, Error>) -> Void) {
AVCaptureDevice.requestAccess(for: .video) { granted in
guard granted else {
completion(.failure(CameraSetupError.denied))
return
}

self.session.beginConfiguration()
self.session.sessionPreset = .photo

guard let device = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video,
position: .back) else {
self.session.commitConfiguration()
completion(.failure(CameraSetupError.missingDevice))
return
}

do {
let input = try AVCaptureDeviceInput(device: device)
guard self.session.canAddInput(input),
self.session.canAddOutput(self.photoOutput) else {
self.session.commitConfiguration()
completion(.failure(CameraSetupError.invalidGraph))
return
}
self.session.addInput(input)
self.session.addOutput(self.photoOutput)
self.session.commitConfiguration()
completion(.success(()))
} catch {
self.session.commitConfiguration()
completion(.failure(error))
}
}
}
}

这段代码能推出三个系统判断。第一,requestAccess 是权限入口,授权结果先于有效相机流。第二,canAddInputcanAddOutput 是 graph 层检查,它们把 App 想要的结构提交给 framework 判断。第三,真正的硬件保留并未在这段代码里展开;它会在 session start、设备锁定、输出激活和系统服务协商时继续发生。

AVCaptureDevice 承担能力选择功能。一个设备可能有 position、device type、format、focus、exposure、zoom、torch、frame rate、depth 等能力。App 可以查询和选择公开属性,但不能把这些属性理解为 sensor 寄存器级控制权。公开属性是系统允许 App 使用的能力表面,系统服务仍会根据硬件状态、权限、热状态和并发访问决定最终结果。

AVCaptureOutput 承担结果形态选择功能。AVCapturePhotoOutput 适合拍照,输出经过 photo pipeline 处理后的结果;AVCaptureVideoDataOutput 适合逐帧处理,输出 CMSampleBufferAVCaptureMovieFileOutput 或相关对象适合录像和文件输出。输出类型的差异会改变系统内部 buffer 数量、编码路径、回调频率和错误表现。

App 层排查 camera request 时,应先固定四个公开证据:authorization status、session graph 是否能添加 input/output、session 是否出现 runtime error 或 interruption、output callback 是否持续产生结果。缺少这些证据时,直接讨论 ISP 或 driver 的内部实现会让问题失去可验证入口。

172.3 Framework Layer:AVFoundation 与 Core Media Sample Buffer

Framework 层把相机硬件能力包装成对象模型和媒体数据模型。AVFoundation 负责 capture graph、设备抽象、输出对象、delegate 回调和错误通知;Core Media 负责时间媒体对象,例如 CMSampleBuffer;Core Video 负责像素 buffer,例如 CVPixelBuffer。这层的作用是把实时硬件流转成 App 能消费的统一数据结构。

CMSampleBuffer 可以理解为“带时间语义的媒体样本容器”。在相机路径中,它常见于 video data output、audio data output 或处理 pipeline。它通常关联一个 image buffer 或 block buffer,并携带 presentation timestamp、duration、format description 和 attachments。App 接收 sample buffer 时,拿到的是 framework 整理后的媒体样本,底层 sensor 曝光时刻、ISP 处理时刻和队列转发细节已经被折叠为公开时间戳和 metadata。

CVPixelBuffer 可以理解为“可被视频、图像、GPU 或编码路径传递的像素内存对象”。它承载像素格式、宽高、平面布局、内存锁定语义和可跨框架传递的 buffer 引用。预览、视频处理、机器视觉、Metal 纹理桥接和编码器输入都可能围绕它组织。App 操作 pixel buffer 时,关注的是格式、生命周期和线程边界;底层物理内存、DMA、cache 同步和 ISP 输出队列由系统路径控制。

AVFoundation 层还有一个容易被忽略的职责:把错误变成 App 可处理的事件。权限拒绝、设备占用、媒体服务重启、session interruption、thermal pressure、配置冲突、输出 back pressure 都不会以同一种形式返回。Framework 会通过 authorization status、notification、delegate error、capture completion handler、session runtime error 等表面暴露不同类别的失败。

下面的表格把 framework 层常见对象放到路径中,而非当作 API 名词孤立记忆。

对象在路径中的位置输入输出判断点
AVCaptureSessionApp 与系统媒体服务之间的 capture graphinput、output、preset、connection运行状态、通知、错误graph 是否可成立,启动是否被中断
AVCaptureDeviceInput设备能力进入 session 的公开入口AVCaptureDevicesession input设备是否存在,权限和占用是否允许
AVCapturePhotoOutput拍照结果输出路径capture settingsphoto result、metadata、errorphoto pipeline 是否完成
AVCaptureVideoDataOutput实时帧输出路径video settings、delegate queueCMSampleBuffer回调频率、格式、back pressure
CMSampleBuffer时间媒体样本image buffer、timing、formatApp 可处理样本时间戳、attachments、format 是否正确
CVPixelBuffer像素内存对象pixel format、dimensions、planes图像处理或编码输入格式、生命周期、线程使用是否安全

Framework 层还决定 App 如何把相机结果接入 UI。Preview 通常通过 AVCaptureVideoPreviewLayer 或更高层封装完成,App 不需要逐帧绘制才能显示相机画面。Preview layer 的存在说明系统优先给低延迟显示路径一个专门表面;App 若改用 video data output 自己处理每帧,就会把一部分延迟、线程调度和 buffer 生命周期责任转移到自己一侧。

因此,Framework 层排查要分清“没有帧”“有帧但处理慢”“有帧但 metadata 异常”“有 photo 结果但保存失败”。没有帧通常要回到 session、权限和设备占用;处理慢通常要看 delegate queue 和 pixel buffer 使用;metadata 异常要看输出 settings 和 format;保存失败要进入媒体持久化路径。

172.4 Privacy Layer:Camera Permission、TCC、Privacy Indicator

Privacy 层把 camera 从硬件能力变成用户授权能力。相机能采集现实世界图像,系统需要在 App 访问前获得用户同意,在访问过程中给出可见提示,并在用户撤销授权后让后续请求返回受限结果。iOS 用户看到的是系统授权弹窗、设置页开关、状态栏或控制中心的隐私指示;App 看到的是 authorization status、request completion、session start 失败或 output 无法产生数据。

TCC 是 Apple 生态中常用于描述 Transparency, Consent, and Control 的名称。在本章语境中,TCC 指系统维护和执行隐私授权状态的控制面,具体数据库、进程和内部检查点按平台私有实现处理。公开可依赖的事实是:App 需要声明 camera usage description,用户授权结果会影响 camera access,系统会在敏感硬件使用时展示隐私提示。

Camera permission 有三个关键位置。第一个位置是安装包元数据:NSCameraUsageDescription 给系统一个向用户解释用途的字符串。第二个位置是运行时授权:App 通过 AVFoundation 查询或请求访问。第三个位置是实际资源打开:系统服务在 session start 或设备激活时仍会依据当前授权状态做有效性判断。把这三个位置分开,能解释“代码已经请求过权限,但 session 仍启动失败”的情况。

隐私层还有撤销路径。用户可以在设置中关闭 camera 权限;App 下次进入相机页面时,authorization status 会变成 denied 或 restricted。此时稳定做法是让 App 停止 session graph 启动,显示可操作说明,并把用户引导到系统设置。继续尝试打开 session 会把错误传播到 framework 和服务层,最终表现为没有预览、capture 失败或 session error。

Privacy indicator 是用户可见结果,不只是 UI 装饰。它说明系统把 camera access 作为运行时状态跟踪,并把敏感硬件占用反馈给用户。对于系统架构读者,这个指示器可以当成观测点:如果 App 认为正在使用 camera,但系统无可见 camera indicator,可能说明 session 没有真正启动;如果 indicator 亮起但 App 没有画面,问题更可能落在输出、preview layer、队列或 UI 展示路径。

隐私层的错误边界需要精确表达。权限拒绝属于用户授权状态;缺少 usage description 属于应用包配置错误;设备被其他高优先级系统任务占用属于资源仲裁;受限模式、家长控制、企业管理或系统策略属于策略状态。它们都会影响 camera request,但排查入口不同。

在 Apple camera path 中,Privacy 层的责任不是传输 pixels,而是回答“这个 App 当前是否有资格请求 camera 能力”。这个判断发生在公开 API 入口,也会在实际资源使用时再次体现。读者排查时应把 authorization status 作为第一证据,把隐私指示器作为运行时观测,把 session/output 错误作为后续证据。

172.5 Service Layer:System Media Service / Daemon 与资源仲裁

Service 层是 App 与硬件之间的能力代理。Apple 没有公开完整的 iOS camera daemon 源码和内部队列,所以本节用 System Media Service / Daemon 表示承担会话管理、设备保留、权限复核、并发仲裁、buffer 分配和错误转发的系统侧责任。这个抽象来自公开 API 行为和移动 OS 的资源所有权模型,而非对私有进程名的确认。

媒体服务接收的不是“打开某个 sensor 寄存器”的命令,而是 framework 提交的 session graph。它需要判断 graph 是否与硬件能力匹配,目标 preset 是否可用,输入输出组合是否可同时满足,当前是否已有占用者,是否存在系统级中断,当前热状态和电量策略是否要求降级。判断通过后,服务才会把资源请求继续下推到驱动和硬件路径。

资源仲裁主要处理四类冲突。第一类是独占冲突,例如另一个 App、系统相机或视频通话已经占用 camera。第二类是组合冲突,例如同一 session 同时要求高分辨率 photo、实时 video data、depth、metadata 和高帧率,超出设备组合能力。第三类是系统优先级冲突,例如来电、锁屏、隐私限制或系统安全任务打断。第四类是运行环境冲突,例如 thermal pressure、低电量、后台状态和应用生命周期变化。

Service 层还负责 interruption 和 recovery。AVFoundation 向 App 暴露 session interruption notification 和 runtime error,并给出某些恢复时机。系统服务内部可能重启媒体进程、释放设备、重新分配 buffer 或通知 session 失效;App 需要响应这些公开事件,暂停 UI、更新状态、在允许时重启 session。这样可以把系统资源变化转换成 App 可处理的生命周期事件。

下面的 Mermaid 图把服务层仲裁写成状态流。它不是 Apple 私有实现图,而是 App 排查时可使用的公开行为模型。

这张图的关键点是:App 的 session 并非只有 running 和 stopped 两种状态。它可能因为权限被拒绝进入 denied,也可能因为 graph 不成立进入 invalid graph,也可能因为设备暂时不可用进入 waiting 或 interrupted。每一种状态都对应不同责任主体:权限由 Privacy 层决定,graph 由 Framework 与 Service 共同判断,busy 和 interruption 由系统资源所有权决定,runtime error 则需要看 service、driver、硬件或媒体输出。

Service 层对开发者可见的证据通常是间接的。App 能看到通知、错误对象、session running 状态、output 回调变化、系统 UI 提示和用户可见占用。读者应把这些证据按照时间顺序排列:授权何时完成,configuration 何时提交,session 何时 start,interruption 何时发生,output 何时停止。时间顺序比单独看一个错误字符串更能定位责任边界。

172.6 Driver / Hardware Layer:Driver Framework、ISP、Sensor

Driver / Hardware 层负责把系统服务的设备请求落到真实 camera module、sensor、image signal processor 和内存 buffer。Apple 对移动设备 camera pipeline 的内部实现保持封闭;公开材料能看到 DriverKit、IOKit、XNU 等驱动框架方向,也能看到 AVFoundation 暴露的设备能力和输出结果,但不能从公开源码还原完整 iPhone camera driver 与 ISP firmware 协议。

Sensor 的职责是采集光信号并生成原始图像数据。它受曝光时间、增益、对焦、帧率、分辨率和硬件同步约束。App 通过 AVCaptureDevice 看到 focus、exposure、white balance、format 等公开控制项;这些项是系统允许调整的能力,不等价于直接写 sensor register。系统会把 App 的设置与当前 format、设备能力、热状态和其他输出需求合并后执行。

ISP 的职责是把 sensor 原始数据处理成可用图像。典型工作包括去噪、白平衡、颜色校正、HDR 合成、镜头校正、锐化、tone mapping、depth 或 portrait 相关处理。App 最终得到的 preview、photo 或 video frame 往往已经经过 ISP 和系统媒体 pipeline 处理。拍照输出的图像质量、metadata 和处理延迟,很多时候取决于 ISP pipeline 与 photo output settings 的组合。

Driver 的职责是连接 OS 和硬件。它处理设备初始化、电源状态、中断、DMA、buffer 队列、错误上报和硬件事件。对于 App 来说,driver 错误不会直接暴露为寄存器或中断信息,而会经由 system service 和 AVFoundation 转成 session error、interruption、device unavailable、capture failed 或 sample buffer 停止。这个转换保护系统稳定性,也限制了 App 对底层故障的可见性。

Buffer 是硬件层和 framework 层的共同接口。相机 pipeline 需要连续产生帧,系统必须在 sensor/ISP 输出、内存、GPU/preview、编码器和 App 回调之间传递 buffer。实时 preview 倾向于低延迟和丢帧可接受;video recording 倾向于持续吞吐和音画同步;photo capture 倾向于多帧合成、metadata 完整和图像质量。不同 output 会改变 buffer pressure 和回调节奏。

Timestamp 是跨层对齐的关键证据。Sensor 采样、ISP 输出、media service 转发、Core Media sample buffer 和 display preview 都涉及时间。App 看到的 CMSampleBuffer timing 信息可以用于判断帧是否连续、是否延迟积压、是否与音频对齐。它不能还原每个硬件阶段的私有时间点,但能帮助定位问题落在实时流、处理队列还是 UI 展示。

Driver / Hardware 层的边界判断应保持克制。可以说“高帧率、高分辨率、HDR、depth、video data output 同时开启会增加硬件和 buffer 压力”,因为这符合公开能力模型和工程逻辑;不应声称某个私有 daemon 调用了某个未公开 driver 函数。对 Apple camera path,正确的写法是用公开 API 和可见结果推断责任边界,再把 sensor、ISP、driver 的内部细节标成私有区域。

172.7 Media Layer:Preview、Capture、Encode、Save

Media 层把相机流变成用户可见内容和持久化文件。Preview、photo capture、video encode、save to Photos 属于不同目标:preview 追求实时显示,photo 追求单张质量和 metadata,video encode 追求连续压缩和同步,save 追求权限、文件格式和 library 事务成功。把它们合成“相机输出”会掩盖许多故障边界。

Preview 是显示路径。App 通常使用 AVCaptureVideoPreviewLayer 把 session 画面连接到 layer tree,系统负责把 camera frame 送到可显示表面。Preview 卡顿可能来自 session 未启动、设备 interruption、主线程阻塞、layer 布局错误、GPU/显示压力或 thermal 降级。排查时先确认 privacy indicator 和 session running,再确认 preview layer 是否绑定正确 session,最后看 UI 线程和渲染路径。

Photo capture 是一次性高质量输出路径。AVCapturePhotoOutput 接收 capture settings,系统可能执行自动曝光、自动对焦、HDR、闪光灯、稳定、语义分割或其他处理,再通过 delegate 返回 photo data、metadata 和错误。拍照按钮点击后出现延迟并不等于 App 卡住;它可能是 photo pipeline 在完成多帧采集、ISP 处理和编码。

Video capture 是持续编码路径。录像需要连续帧、音频同步、编码器吞吐、文件写入和磁盘空间。它比 preview 多了编码和写文件压力,比 photo 少了单张高质量处理的部分等待。录像失败常见责任边界包括 session interruption、encoder 无法持续接收帧、磁盘空间不足、文件写入错误和应用进入不合适生命周期状态。

Video data output 是 App 自己处理帧的路径。它给 App CMSampleBuffer,适合视觉算法、实时滤镜、扫码或自定义编码。App 需要管理 delegate queue、pixel buffer 生命周期、处理耗时和 back pressure。处理耗时超过输入帧率时,系统可能丢帧或积压,最终表现为回调不稳定、预览延迟或输出质量下降。

Save 路径需要单独看。App 可以把照片写入自己的 sandbox 文件,不需要 Photos library 写入权限;如果要把结果保存到用户 Photos library,则需要通过 Photos framework 执行授权和 library change。当前 iOS 的 Photos 权限模型还区分读写、受限选择和添加能力等公开行为,具体 API 使用以 PHPhotoLibrary 文档为准。Camera 权限通过不代表 Photos 保存一定成功。

Media Layer 的判断顺序可以固定为:先判断 preview 是否代表实时 camera stream 已经建立,再判断 capture delegate 是否收到结果,再判断 encode 或 image data 是否完整,再判断保存目标是 sandbox 还是 Photos library,最后根据错误归因到权限、文件、编码、媒体库事务或系统中断。这个顺序能解释“预览正常但保存失败”“拍照成功但相册没有照片”“video data output 有帧但 photo capture 失败”等常见组合。

172.8 Apple Camera Path 中的封闭硬件能力与 Public API 边界

Apple camera path 的学习难点在于公开 API 很清楚,内部硬件路径高度封闭。可公开讨论的对象包括 AVCaptureSessionAVCaptureDevice、outputs、authorization、Core Media buffer、Photos 保存、通知和错误;需要谨慎处理的对象包括具体 daemon 名称、driver 函数、ISP firmware 算法、sensor register、硬件队列和调度策略。

Public API 边界并不妨碍系统路径分析。App 能观察到 authorization status、usage description 配置、session running、interruption reason、runtime error、device format、output settings、sample buffer timing、photo metadata、Photos save result。这些证据足够建立责任链:App 意图由 session graph 表达,Framework 负责对象模型和媒体数据,Privacy 负责授权状态,Service 负责资源仲裁,Driver / Hardware 负责物理采集和处理,Media Layer 负责展示、编码和保存。

封闭硬件能力需要通过“公开信号 → 责任边界 → 可能原因”的方式分析。例如高分辨率 photo 与实时 video data output 同时使用时,App 可以观察到 configuration 是否被接受、frame rate 是否下降、photo capture 是否延迟、sample buffer 是否丢帧。根据这些信号,可以判断系统正在处理硬件组合和 buffer 压力;具体 ISP 队列如何调度属于私有区域。

下面的表格给出 Apple camera path 的材料分层,帮助读者把事实和推断分开。

层级可公开依赖的材料可做出的判断需要标注边界的内容
AppSession 配置代码、authorization status、delegate 回调App 是否正确描述请求App 无法证明硬件已经按预期工作
FrameworkAVFoundation、Core Media、Core Video 文档和对象行为graph、buffer、timestamp、错误表面内部服务调用细节不公开
Privacyusage description、request access、系统隐私提示用户授权是否允许 camera access授权存储和内部进程实现不展开
Serviceinterruption、runtime error、资源占用表现资源仲裁和会话状态变化daemon 名称和私有协议谨慎表达
Hardware设备能力、format、frame rate、output 结果sensor/ISP 能力被系统包装driver 函数、ISP firmware、寄存器路径不可公开验证
Mediaphoto data、sample buffer、encoded file、Photos result输出和保存路径是否成功Photos 内部数据库事务不展开

这种分层能防止两个常见错误。第一个错误是把 AVFoundation API 当成纯 App 内部库,忽略系统服务和权限仲裁。第二个错误是把 Apple 私有硬件路径当成已知源码路径,给出无法回溯的函数名或队列名。稳定的写法是在公开 API 和可见行为内完成责任定位,把私有硬件能力写成边界清楚的推断。

回到贯穿材料:用户点击拍照时,App 并未直接控制 sensor;它向 AVFoundation 提交 capture 意图。系统确认授权,媒体服务保留设备,驱动和 ISP 产生并处理图像,Core Media 和 AVFoundation 把结果返回,App 再选择 preview、编码或保存。理解这条链路后,读者就能把“黑屏、权限拒绝、设备占用、预览延迟、拍照慢、保存失败”分别放到不同责任边界中分析。

最小自检任务

某个 iOS App 的相机页面出现如下现象:用户首次进入时授权弹窗正常出现,用户允许 camera 访问;页面上方隐私指示器亮起,但预览区域黑屏;点击拍照按钮后没有照片保存到相册。请按照本章路径写出排查顺序,区分权限、session graph、系统服务、preview、capture output 和 Photos 保存各自的判断点。

答案要点

首先确认 camera 权限已经授予,并确认 NSCameraUsageDescription 存在。隐私指示器亮起说明系统认为 App 正在访问 camera,因此问题重点从初始授权转向 session、preview 和输出路径。

接着检查 AVCaptureSession 的 input、output 和 preset 是否能成立,确认 canAddInputcanAddOutputcommitConfigurationstartRunning() 之后没有 runtime error 或 interruption。若 session 出现 interruption,要把责任放到系统服务资源仲裁,例如设备被占用、系统打断或生命周期变化。

然后检查 preview layer 是否绑定同一个 session,layer frame 是否正确,UI 是否在主线程完成布局。隐私指示器亮起但黑屏,常见边界是 preview 展示路径、layer 绑定、session 输出连接或 UI 渲染,而非初始授权。

再检查拍照 output。确认 AVCapturePhotoOutput 已加入 session,capture settings 与当前设备能力匹配,delegate completion 是否返回 photo data 或 error。若 preview 修复后仍无照片,要把问题分成 photo capture 是否成功和保存是否成功两段。

最后检查 Photos 保存路径。Camera 权限只允许采集图像;保存到用户相册需要 Photos framework 的授权和 library change 成功。若 photo data 已生成但相册没有照片,应检查 Photos 权限、保存事务、错误回调和目标是否实际写入 sandbox 文件。

本章知识点总结

  • 请求主线:Apple camera request 从 App 意图进入 AVFoundation,经隐私授权和系统媒体服务仲裁,再到 driver、ISP、sensor 和媒体输出。
  • Session GraphAVCaptureSession 表达 input、output、preset 和 connection 的组合,系统依据这张图判断请求是否可执行。
  • 设备抽象AVCaptureDevice 暴露系统允许 App 查询和设置的能力表面,不代表 App 直接控制 sensor 寄存器。
  • 输出差异:photo、video、preview 和 sample buffer output 对延迟、质量、编码、buffer 和回调频率有不同约束。
  • 媒体容器CMSampleBuffer 承载带时间语义的媒体样本,CVPixelBuffer 承载可跨框架传递的像素内存。
  • 隐私入口:Camera access 需要 usage description、运行时授权和实际资源打开时的有效检查共同成立。
  • 可见指示:Privacy indicator 是系统运行时跟踪 camera access 的用户可见证据,可用于判断 session 是否真正触达敏感硬件能力。
  • 服务仲裁:系统媒体服务负责设备保留、session 状态、并发冲突、interruption、runtime error 和恢复路径。
  • 硬件边界:Sensor、ISP、driver 和 firmware 的内部协议属于 Apple 私有实现,正文应基于公开信号推断责任边界。
  • Buffer 压力:高分辨率、高帧率、实时处理和多输出组合会增加 buffer 与硬件调度压力,表现为延迟、丢帧或输出失败。
  • 保存边界:Camera 权限允许采集图像,保存到 Photos library 还需要 Photos framework 的授权和保存事务成功。
  • 排查顺序:相机问题应按权限、session graph、资源仲裁、preview、capture output、编码保存的顺序定位。
  • 公共证据:authorization status、session notification、runtime error、delegate callback、sample timing 和保存结果是 Apple camera path 的主要公开证据。
  • 私有推断:对 daemon、driver、ISP 队列和 sensor 协议只能做边界清楚的推断,不能写成公开源码事实。