Apple 的采集设备:AVCapture 音视频

从零做一个全平台播放器·约 1516 字

Apple 平台上采画面与采声音走的是同一套采集会话:挂不同的输入设备、挂不同的输出,回调走同一种委托。所以这一侧的结构比另外两个平台更整齐——差别集中在格式与方向怎么定、以及设备失效之后谁负责恢复。这篇写这套会话怎么搭、格式白名单只有一种、以及关流为什么不停会话。

一套会话,两个源

一套会话两个源,与输入输出的挂载流程

相机与麦克风各有一个管理器,各自建一个采集会话,并把会话与一条串行队列注入到每个源对象里。

挂输入的动作有固定顺序:开会话配置、把已有输入全部摘掉、按设备建输入、先问能不能加、再加进去、提交配置。摘光的做法是为了重配:每次打开都重新搭一遍。

输出那一侧有个复用判断:如果这个源还没有输出,就建一个视频或音频数据输出;已经有了就复用——复用意味着换源不必重建输出,委托与队列也不用重挂。然后把委托与那条串行队列挂上去,数据从委托进来。

委托是独立的类,只做一件事:把回调转给 C++ 的成员函数。这一层是为了让核心代码不沾委托协议。两个源建好后各自注册进视频与音频的设备槽——那张表与另外两个平台是同一张,上层按槽位取用,不必感知底下是哪套会话。

格式与方向

相机的可选格式来自设备自己报的那张表。这一侧同样只收一种平面格式(两个范围变体),其余不要;挑档走的是与另外两端同一套打分,默认目标按设备方位给——前置与后置各一套,分辨率与帧率一起给,其它方位取候选表里的第一项。

会话的预设分辨率是另一套固定档,设置之前先问一句能不能设,问不过就保持原样;上层要的分辨率在候选表里挑最近的一档。两套各自独立,可能出现实际输出与登记的尺寸不一致。

输出那边把像素格式定死成那一种平面格式,不留给系统挑。方向上前置镜像、后置不镜像,方向本身是硬设的——多设备各不相同方向的处理这一版没有做完。

回调、队列与丢帧

委托挂在一条串行队列上,等于同一时刻只有一帧在通道里,前一段没处理完,后一段就在队列里等着。迟到帧丢不丢由系统默认策略决定,这一侧没有显式设置。

丢帧不上报:回调里只对空样本直接返回,没有计数、也没有错误上报——这一层丢了多少、为什么丢,下游无从知道,只能自己数。这一点与 Android 那一侧不同,那边超时至少记一条日志。

采集拿到的时间戳其实被算过一次,但真正填进帧的是本地时钟那一刻的值。也就是说这一侧帧上的时间来自本地时钟,不是采集给的呈现时间——到编码那一篇会看到另一侧是怎么处理时间戳的。

设备失效之后

格式与方向怎么定,以及设备失效后的行为

打开是「拆光重挂」:每次打开都先摘掉全部输入再重建,输出能复用就复用。

关流不停止会话,只把委托解绑。这样做是为了换相机的时候能秒切——会话还在跑,换的只是输入。常驻也有代价:设备一直被这套会话占着,与其它要用相机的应用怎么相处,这一版没有实测过,当下是按「换相机要快」取的舍。

进后台靠应用生命周期的通知:收到要失去活动状态的通知就置一个全局标志,回到活动再清掉。这是全局层面的进出后台,不是采集层的会话重配。

这里没有监听会话中断与设备拔插。也就是说相机被别的进程抢走、或者被系统中断时,这一层不会自己重配或重开。

三端采集对比

三条采集路在三个平台上的分布不一样,但收口处一样。

Windows 那边:屏幕与窗口共用一套抓取实现,主路是系统那套图形捕获、回退是 GDI;摄像头单独走一套媒体基础;声音两套,麦克风事件驱动、声卡回环轮询。

Android 那边:相机走 NDK 那套相机接口,输出面自己建;声音反过来落在 Java 侧的录音类上,同步读。

Apple 这一侧没有屏幕采集那一支——三条路里只有 Windows 有屏幕;相机与麦克风全落在同一套采集会话上,只是输入设备与输出类型不同。

三端一致的地方是收口:交出来的帧只有三种形态——纹理帧、CPU 平面帧、PCM 数据块;都汇到设备源那一个口子,再经原始帧源交给上层;设备失效都只上报、不自愈。

采到的帧:直接装系统给的像素缓冲

这一侧交出去的帧里装的直接就是系统给的像素缓冲,格式标成平面那一档。它的生命周期由那个样本管着,这一层不用手动放。

渲染那一侧拿到它之后按像素缓冲处理,直接映射成纹理——所以从采集到渲染这一段没有额外的拷贝。

音频那条同理,只是输出换成音频数据输出;音频描述从设备报的第一个采样描述转过来,采样率、声道数都从那一份描述取。

这里有一处与相机不同:相机的档是遍历设备格式筛出来的,音频只取设备报的第一个采样描述——音频设备的采样率与通道数一般就那么一档,不必筛。

关流的顺序是:先停任务线程,再解绑委托(不停止会话),真正清理时才摘输入。

引擎 avox 在 GitHub 开源:github.com/xxxzhou/avox,这套采集会话的搭建与委托桥都在里面。

音视频开发 播放器 AVCapture 采集 iOS macOS