相机应用会请求一条取景器视频流,有时调整其尺寸,挂接内存,再把一次捕获排入队列。底层硬件可以是直接输出可用帧的 USB 网络摄像头,也可以由一枚传感器把数据送入 CSI-2 接收器、图像信号处理器和数条 DMA 通路,再配上一组操作顺序由具体片上系统决定的控制项。这两套硬件形态对应着截然不同的设备模型。
libcamera 选择让应用程序面对统一模型,将各类拓扑细节留在平台层。硬件差异依然存在,必须由明确的组件负责。在 Laurent Pinchart 的平台支持演讲中,这份职责由两部分分担:pipeline handler(流水线处理器)理解特定硬件处理链如何搬运数据,图像处理算法模块(Image Processing Algorithm module,IPA 模块)则把图像统计数据换算成曝光、白平衡等设置。[1][2]
演讲借一块 NXP 开发板展开,重点落在可移植性止步的位置,内容也因此超出单块开发板的实现配方。libcamera 向应用程序提供通用的请求模型;每个平台实现还要证明,它能够把应用意图翻译成有效配置、缓冲区流转、控制项与完成事件,并把设备图始终留在平台层内。
Linux Foundation 在官方频道发布了这段 Embedded Open Source Summit 2023 录像。Pinchart 是 libcamera 创始人之一,也是长期参与 Linux 媒体子系统开发的工程师;演讲中,他以 NXP i.MX8M Plus Image Sensing Interface 为基础搭起一个最简 pipeline handler。等代码表面上已经能传输画面,他又继续追查尚未解决的故障。[1]
首先,/dev/video0 只占整台相机的一部分
演讲开头先回到相机架构的历史演变。传统网络摄像头或电视采集卡可以只提供一个捕获节点和一组控制项,应用程序协商格式、把缓冲区排入队列,再接收图像帧。现代嵌入式相机把这些工作分散到多处:传感器生成原始数据,接收器接纳数据,ISP 执行色彩与空间处理,多条输出通路还可分别给出预览、全分辨率图像、原始数据或统计数据。
Linux Media Controller 与 V4L2 API 把这些部件逐一暴露出来。控制这些硬件离不开这种开放程度,应用程序若直接把这些底层 API 当作契约,代价却很沉重。现行 libcamera 文档用一幅真实媒体图说明了这个问题,其中包含传感器、子设备、缩放器、参数队列、统计队列与视频节点。在更简洁的应用侧示例中,程序可以枚举一个 Camera 对象,再查询它支持哪些数据流。[2] LWN 对 libcamera 早期演讲的报道也记录了同一股设计压力:内核接口让复杂硬件变得可寻址,可移植应用仍需要另一层来协调这些部件。[7]
这是视频给出的第一条分界。内核驱动继续负责原有工作,不同相机的能力差异也照原样保留。libcamera 所做的,是把硬件媒体图转成相机级契约,并让应用程序查询和验证其中的限制。
pipeline handler 是熟悉特定平台的翻译层
当 Pinchart 从通用架构转入 i.MX8M Plus 示例,pipeline handler 的职责开始变得具体。它必须识别正确的媒体实体,创建相机及其数据流,生成合理配置,并验证应用程序所作的修改。随后还要配置硬件、准备缓冲区、启动和停止设备、把请求排入队列并报告完成状态。
其中每个动作都依赖平台知识。请求宽度是否需要对齐,要看具体硬件;libcamera 的像素格式有时要换成设备要求的 V4L2 表示。两条输出流会不会争用同一个共享处理单元,同样由硬件布局决定;缓冲区有时还要经过接收器与 ISP 之间的内部池,才能进入应用程序可见的内存。因此,现行编写指南把配置验证交给具体的 pipeline handler:它可以接受请求,将其调整为受支持的结果,或者判定请求无效并拒绝。[4]
因此,pipeline handler 与第二套内核驱动属于不同层次,零散的产品怪癖也概括不了它。内核驱动公开设备和操作,handler 则把这些操作组合成 libcamera 承诺的上层生命周期。以格式为例,硬件若在验证后悄然改动格式,handler 应把它视为自身契约已经破裂;这项意外应止于 handler 这一层。[4]
Request 是意图的基本单位
应用程序依赖的稳定单位是 Request,它把操作提升到“读取相机文件”之上。一个请求至少会把数据流与 FrameBuffer 关联起来,也可以携带这次捕获使用的控制项。应用程序通常保持数个在途请求,这样在消费并回收已完成缓冲区时,硬件处理链仍可继续运转。[3][4]
这套安排的作用远超 API 整洁。多流相机可以把同一次捕获中的预览缓冲区和静态图像缓冲区关联起来;逐帧控制项可以和它所影响的内存一同传递;各缓冲区可以先后完成,等它们全部就绪后再最终完成请求;元数据(metadata)也可以随结果保留,取代事后从全局状态所作的推断。[3]
pipeline handler 向下翻译这份意图。它找到每条数据流对应的缓冲区,在正确的硬件环节应用请求携带的控制项,再把缓冲区排给负责生成图像帧的设备。硬件完成工作后,handler 又向上翻译:先把各个缓冲区标为完成,等请求内的每个缓冲区全部完成后,再把整个请求标为完成。底层工作即使跨越多个设备与异步事件,libcamera 仍会为应用程序保持请求的完成顺序。[3][4]
最具启发性的时刻,是一场迟迟没有完成的捕获
演示临近结尾时,看起来已经大功告成。handler 匹配到相机,配置成功,设备也开始传输。cam 工具宣布将捕获五帧,随后就停在那里等待。
这正是整场演讲最有分量的工程启示:缺失环节没有触发醒目的崩溃,程序只是停住。前向通路已经齐全,包括分配或导入缓冲区、开启数据流,以及把任务排入队列;返回通路仍是空白。捕获设备的 bufferReady 信号尚未连接到回调,程序因而无法找回缓冲区所属的请求,也无法调用 pipeline handler 的完成函数。接通这条事件通路后,同一条命令随即报告五帧,速率约为每秒 60 帧。[1]
一帧是否完成,要看抽象层是否完成了全部记账。光子抵达硅片、DMA 引擎写入字节,或某处文件描述符变得可读,都只覆盖其中一步。完整的完成状态还要求正确的缓冲区与正确的请求相连,元数据已经最终确定,请求中所有必需数据流对应的缓冲区都已完成,并且应用程序收到完成信号。现行指南用 completeBuffer() 与 completeRequest() 两个独立操作,保留了这项严格区分。[4]
这项区分也指向更好的测试。一套新的 handler 应覆盖取消和关闭,也应覆盖稳定捕获;还要测试多个在途请求,平台支持多流时也要同时测试多条数据流。格式调整、入队错误、仍有任务时停止设备,以及每个缓冲区是否恰好返回一次,同样需要覆盖。第一帧成功返回,对生命周期正确性的证明仍然很有限。
IPA 模块把图像判断从设备调度中分离出来
演讲继续追踪 ISP 统计数据流向图像处理算法模块的过程,第二处分工也随之出现。pipeline handler 负责设备的操作顺序,IPA 负责计算图像处理决策。典型的反馈回路会把某一帧生成的统计数据送给自动曝光、自动增益或自动白平衡算法,再把算法所得参数应用到后续帧。
像素传送与图像判断的演进节奏不同,这项分工适应了二者各自的变化。同一种 ISP 设计可以跨多款 SoC 复用,不同摄像头模组则需要不同的调优数据。有些厂商会公开自己的算法;另一些厂商也可以提供专有模块。libcamera 在 handler 与 IPA 之间定义可序列化接口,让交互可以跨越进程。现行指南规定,启动后的实时路径调用必须采用异步方式;自动生成的代理(proxy)则把模块是在进程内还是隔离环境运行的差别藏在接口之后。[5]
这条分界有明确用途,作用也有清楚限度。把封闭模块放进沙箱(sandbox),可以限制代码接触的资源;调优内容仍处于黑箱内,画质仍需单独验证。完全开源的算法同样依赖传感器特征测定与硬件专用调优。Raspberry Pi 在 2020 年完成的集成展示了更完整的形态:V4L2 驱动、libcamera pipeline handler 与开放的 3A 算法取代了原先专有控制路径中的大部分环节,同时保留了有文档说明的平台衔接点。[6]
正式列为已支持平台之前要核验什么
演讲以一次成功捕获收尾;五帧只代表演示跑通,距离生产级支持还有更多工作。从这套设计出发,审查清单也要更加严格:
- 枚举:匹配过程是否只选择目标媒体图,并覆盖装有多个相机或带有相似实体的系统?
- 协商:生成的配置是否描述真实能力,验证结果是否清楚列出每一项调整?
- 内存:用例需要零拷贝交换时,缓冲区能否来自应用程序或另一个设备;内部池是否有容量上限并能正常释放?
- 逐帧行为:即使传感器与 ISP 存在延迟,控制项是否仍应用到预定帧,并返回相应元数据?
- 完成:成功、错误、取消、停止与重启等通路,是否都会让每个在途请求恰好结束一次?
- 算法隔离:handler–IPA 协议是否可序列化,捕获通路是否异步,并且在启用隔离时仍保持同样正确的行为?
演讲提到的 lc-compliance 套件可以作为起点;目视检查与平台专用压力测试仍然重要。[1] libcamera 抽象的价值,正来自它把这些责任集中到一起。单纯隐藏所有差异,缺少的正是显式翻译这一步。应用可移植性要求一个由具体平台负责的组件,把各种差异翻译成显式的配置、请求和完成语义。
视频看完,再看封面照片。传感器位于排线一端,处理器、内存和连接器则在另一块板上。[8] 这两件实物与应用程序的 Request 之间,正是演讲揭示的全部工作。一个 libcamera 帧除了图像本身,还记录了一份证明:每一层都对图像应是什么达成一致,而且这份约定完整走完了返程。
来源
- The Linux Foundation,"Learn How to Support Your SoC and ISP in Libcamera — Laurent Pinchart, Ideas on Board",Embedded Open Source Summit 2023,YouTube 视频。
- libcamera 文档,"Introduction" 与现行相机软件栈概览。
- libcamera 文档,"Using libcamera in a C++ application",现行应用程序编写指南。
- libcamera 文档,"Pipeline Handler Writer's Guide",现行设备集成与完成模型。
- libcamera 文档,"IPA Writer's Guide",现行 handler–algorithm 协议与隔离模型。
- David Plowman,"An open source camera stack for Raspberry Pi using libcamera",Raspberry Pi,2020 年 5 月 4 日。
- Jonathan Corbet,"Access to complex video devices with libcamera",LWN.net,2019 年 7 月 25 日。
- ZippeyKeys12,"Raspberry Pi with Camera Module",Wikimedia Commons,拍摄于 2020 年 4 月 17 日,CC BY-SA 4.0。