ESC
输入关键词搜索文章标题和内容

libcamera 一统江湖:如何用一份代码横跨 Linux 与 Android 摄像头生态

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

libcamera 一统江湖:如何用一份代码横跨 Linux 与 Android 摄像头生态


导读

告别 V4L2 的碎片化 API,告别 Android HAL 的厂商私有实现。libcamera 作为 Linux 官方摄像头框架,用统一抽象层同时对接 V4L2 内核驱动与 Android Camera HAL,让摄像头应用「一次编写、到处运行」。本文深入其四层架构、Pipeline Handler 机制与adaptation生态,并给出可直接复现的 C++ 示例。


一、原理简析

现代摄像头早已不是「一个传感器 + 一个输出」这么简单。一条典型的硬件管线涉及:CMOS 传感器 → MIPI CSI-2 接收器 → ISP(图像信号处理器)→ 多个 Resizer(输出不同分辨率)。在内核层,这些组件通过 Linux Media 子系统(V4L2 + Media Controller)暴露为多个 /dev/v4l-subdev* 和 /dev/video* 节点,每个都需要独立配置。

直接使用 V4L2 的问题是:不同厂商的管线拓扑完全不同,应用必须了解硬件细节才能正确初始化。换到另一块 SoC,代码基本重写。

Android Camera HAL 则面临另一个困境:HAL 层是厂商私有实现,代码耦合在 /vendor 分支,难以维护。Android Treble 架构虽用 HIDL 解耦,但厂商适配负担依然沉重。

libcamera 的核心思路是:在内核 V4L2 之上构建一层统一抽象,对上暴露简洁的 Camera API,对下通过可插拔的 Pipeline Handler 适配不同硬件平台。目前生态已覆盖 Linux(V4L2 应用)、Android(HAL v3)、ChromeOS、GStreamer(libcamerasrc)和 Python 绑定。


二、架构全景图

libcamera 整体分为四层,从上到下依次为应用层 → Adaptation Layer → libcamera Framework → Kernel Drivers:

flowchart TB subgraph 应用层["🔵 应用层"] APP1["原生 V4L2 应用<br/>(LD_PRELOAD 劫持)"] APP2["Android Camera2 API"] APP3["GStreamer<br/>libcamerasrc"] APP4["Python 应用<br/>libcamera Python绑定"] APP5["原生 C++ 应用<br/>libcamera API"] end subgraph 适配层["🟢 Adaptation Layer(适配层)"] V4L2["V4L2 兼容层<br/>LD_PRELOAD 劫持"] HAL["Android Camera HAL v3<br/>基于 libcamera 实现"] GS["GStreamer Element<br/>libcamerasrc"] PY["Python Bindings"] end subgraph 核心框架["🟣 libcamera Framework"] CM["Camera Manager<br/>设备枚举/热插拔"] CAM["Camera<br/>核心抽象"] PH["Pipeline Handler<br/>硬件管线抽象"] IPA["IPA Module<br/>图像处理算法"] end subgraph 内核层["🔴 Kernel Drivers"] SENSOR["Camera Sensor<br/>/dev/v4l-subdevX"] CSI["CSI-2 Receiver<br/>/dev/v4l-subdevX"] ISP["ISP<br/>/dev/v4l-subdevX + videoX"] MEDIA["Media Controller<br/>/dev/mediaX"] end APP1 -.->|"LD_PRELOAD<br/>无需修改源码"| V4L2 APP2 -.->|"HIDL"| HAL APP3 -.->|"GStreamer<br/>插件"| GS APP4 -.->|"Python bindings"| PY APP5 -.->|"C++ API"| CAM V4L2 -.->|"统一调用"| CAM HAL -.->|"底层实现"| CAM GS -.->|"底层实现"| CAM PY -.->|"底层实现"| CAM CAM --> CM CM --> PH PH --> IPA PH --> SENSOR PH --> CSI PH --> ISP PH --> MEDIA IPA -.->|"算法参数调节"| PH style 应用层 fill:#E3F2FD,stroke:#1976D2 style 适配层 fill:#E8F5E9,stroke:#388E3C style 核心框架 fill:#F3E5F5,stroke:#7B1FA2 style 内核层 fill:#FFEBEE,stroke:#D32F2F style CM fill:#F3E5F5,stroke:#7B1FA2 style CAM fill:#F3E5F5,stroke:#7B1FA2 style PH fill:#F3E5F5,stroke:#7B1FA2 style IPA fill:#F3E5F5,stroke:#7B1FA2

三、核心流程图

3.1 libcamera 内部 Pipeline 数据流

从镜头光线到应用拿到帧数据,libcamera 内部经历如下管线:

flowchart TB subgraph 硬件["🔴 硬件层"] SENSOR["Camera Sensor<br/>图像曝光/ADC/读取"] CSI["MIPI CSI-2<br/>高速串行传输"] end subgraph 核心处理["🟣 libcamera 核心处理"] PH["Pipeline Handler<br/>根据硬件注册"] ISP["ISP<br/>黑电平校正/坏点修复<br/>镜头阴影校正/去噪<br/>白平衡/去马赛克<br/>色彩校正矩阵/Gamma"] IPA["IPA Module<br/>3A算法(AE/AWB/AF)<br/>图像增强算法"] STREAMS["Stream Manager<br/>多路输出管理"] end subgraph 输出目标["🟢 输出目标"] DRM["DRM Display<br/>零拷贝显示"] ENCODE["Video Encoder<br/>H.264/JPEG 编码"] MEM["Memory<br/>用户空间缓冲区"] end SENSOR -->|"原始RAW"| CSI CSI -->|"RAW"| PH PH -->|"配置数据流"| ISP ISP -->|"处理后图像"| IPA IPA -->|"3A+增强"| STREAMS STREAMS -->|"Stream 0 1080p"| DRM STREAMS -->|"Stream 1 4K"| ENCODE STREAMS -->|"Stream 2 缩略图"| MEM style SENSOR fill:#FFEBEE,stroke:#D32F2F style CSI fill:#FFEBEE,stroke:#D32F2F style PH fill:#F3E5F5,stroke:#7B1FA2 style ISP fill:#F3E5F5,stroke:#7B1FA2 style IPA fill:#F3E5F5,stroke:#7B1FA2 style STREAMS fill:#E3F2FD,stroke:#1976D2 style DRM fill:#E8F5E9,stroke:#388E3C style ENCODE fill:#E8F5E9,stroke:#388E3C style MEM fill:#E8F5E9,stroke:#388E3C

3.2 V4L2 兼容层 vs Android HAL 路径对比

flowchart TB A(["应用发起摄像头请求"]) --> B{"选择接入方式"} B -->|"原生 libcamera"| C["CameraManager<br/>统一接口"] B -->|"V4L2 应用<br/>不愿改代码"| D["V4L2 兼容层<br/>LD_PRELOAD 劫持"] B -->|"Android Camera2 API"| E["Android Camera HAL v3<br/>基于 libcamera"] D -->|"重定向 ioctl<br/>到 libcamera"| C E -->|"HIDL 调用<br/>libcamera 实现"| C C --> F["Pipeline Handler<br/>自动识别硬件拓扑"] F --> G["配置 Sensor → ISP → Stream"] G --> H["开始帧采集"] H --> I["帧数据写入<br/>用户缓冲区"] I --> J(["完成"]) style A fill:#E3F2FD,stroke:#1976D2 style J fill:#E3F2FD,stroke:#1976D2 style C fill:#F3E5F5,stroke:#7B1FA2 style D fill:#FFF8E1,stroke:#F57C00 style E fill:#FFF8E1,stroke:#F57C00 style F fill:#F3E5F5,stroke:#7B1FA2 style G fill:#F3E5F5,stroke:#7B1FA2 style H fill:#F3E5F5,stroke:#7B1FA2 style I fill:#E8F5E9,stroke:#388E3C

四、实操步骤

4.1 应用开发标准流程(5 步)

使用 libcamera C++ API 开发应用,标准流程分为 5 步:

flowchart TB S(["开始"]) --> A["Step 1:创建 CameraManager"] A --> B["Step 2:获取 Camera 列表"] B --> C["Step 3:配置 Stream<br/>格式/分辨率/帧率"] C --> D["Step 4:请求帧缓冲区<br/>并关联 DMA-BUF"] D --> E["Step 5:启动 Pipeline<br/>循环 DQBUF 处理"] E --> F(["结束"]) style S fill:#E3F2FD,stroke:#1976D2 style A fill:#E3F2FD,stroke:#1976D2 style B fill:#E3F2FD,stroke:#1976D2 style C fill:#E3F2FD,stroke:#1976D2 style D fill:#E3F2FD,stroke:#1976D2 style E fill:#E3F2FD,stroke:#1976D2 style F fill:#E3F2FD,stroke:#1976D2

4.2 核心代码示例(C++)

以下代码展示 libcamera 最简使用流程,从枚举摄像头到输出帧:

// 依赖:libcamera-dev, C++17
// 编译:g++ -std=c++17 cam_demo.cpp -o cam_demo -libcamera

#include <libcamera/libcamera.h>
#include <iostream>
#include <memory>

using namespace libcamera;

int main() {
    // Step 1:创建 CameraManager(单例)
    std::unique_ptr<CameraManager> cm = std::make_unique<CameraManager>();
    cm->start();

    // Step 2:获取所有可用摄像头
    for (auto const &camera : cm->cameras()) {
        std::cout << "摄像头: " << camera->id() << std::endl;
    }
    if (cm->cameras().empty()) {
        std::cerr << "未检测到摄像头" << std::endl;
        return 1;
    }

    // Step 3:获取第一个摄像头并配置
    std::shared_ptr<Camera> camera = cm->cameras()[0];
    camera->acquire();

    std::unique_ptr<CameraConfiguration> config =
        camera->generateConfiguration({ StreamRole::Viewfinder });
    StreamConfiguration &streamConfig = config->at(0);
    streamConfig.pixelFormat = PixelFormat::fromString("NV12");
    streamConfig.size = { 1920, 1080 };
    streamConfig.bufferCount = 4;  // 4 帧缓冲区

    camera->configure(config.get());

    // Step 4:申请帧缓冲区(可绑定 DMA-BUF)
    std::vector<std::unique_ptr<FrameBuffer>> buffers;
    for (unsigned i = 0; i < streamConfig.bufferCount; ++i) {
        std::unique_ptr<FrameBuffer> buf = camera->createBuffer(i);
        buffers.push_back(std::move(buf));
    }

    // Step 5:启动 Pipeline,循环取帧
    std::unique_ptr<Request> request = camera->createRequest();
    request->addBuffer(streamConfig.get(), buffers[0].get());

    camera->start();
    camera->queueRequest(request.get());

    // 在实际应用中:监听 request->complete 信号,取出 FrameBuffer 处理
    // 这里演示核心 API 调用路径

    camera->stop();
    camera->release();
    cm->stop();

    return 0;
}

4.3 GStreamer 一行命令验证

不想写代码?直接用 GStreamer 调用 libcamera:

来自 linuxros.cn · linuxROS
# 安装:sudo apt install libcamera-gstreamer gir1.2-libcamera
# 查看可用摄像头
gst-device-monitor-1.0 Video/Source

# 零延迟取景预览(Wayland 显示)
gst-launch-1.0 libcamerasrc camera-name='imx415 1-001a' ! \
    video/x-raw,format=NV12,width=1920,height=1080 ! \
    waylandsink

# 录制到文件
gst-launch-1.0 libcamerasrc camera-name='imx415 1-001a' ! \
    video/x-raw,format=NV12 ! v4l2h264enc ! \
    h264parse ! mp4mux ! filesink location=output.mp4

五、对比表格

Linux 摄像头方案演进对比

方案一:直接使用 V4L2 API
应用直接调用 V4L2 ioctl 序列
优点:零依赖、无抽象损耗
缺点:每款 SoC 管线不同,代码不可移植
↓
方案二:GStreamer v4l2src
通过 v4l2src 插件接入 GStreamer 管道
优点:管道化、可串联编解码/显示插件
缺点:仍需处理厂商私有 ISP 参数
↓
方案三:libcamera ⭐推荐
统一抽象层 + Pipeline Handler 适配不同硬件
优点:一份代码支持 Linux/Android/GStreamer
缺点:学习曲线略高、需要厂商提供 Pipeline Handler

六、常见问题解决

Q1:摄像头枚举为空,camera->id() 什么都没有?

检查 Pipeline Handler 是否注册:ls /usr/lib/libcamera/*_soft.so。部分平台需要额外安装 IPA 模块(如 Raspberry Pi 的 libcamera-apps)。

Q2:LD_PRELOAD 劫持 V4L2 后,原有 V4L2 应用报错?

V4L2 兼容层仅处理标准 ioctl,厂商私有扩展可能被忽略。建议逐步将应用迁移到 libcamera C++ API 或 GStreamer libcamerasrc。

Q3:Android 上 Camera2 API 调用慢、延迟高?

确认使用的是基于 libcamera 的 HAL,而非原生厂商 HAL。可通过 getpackagename 查看 Camera 服务进程名称。libcamera HAL 支持 Zero-Copy Buffer 传递,可大幅降低延迟。


七、总结

libcamera 通过 Camera Manager 统一设备枚举、Pipeline Handler 抽象硬件管线、IPA Module 解耦图像算法,实现了 Linux 摄像头生态的真正统一。核心优势有三:V4L2 应用无需改代码(LD_PRELOAD 兼容)、Android Camera HAL 基于同一框架(省去重复适配)、GStreamer 原生集成(一条命令完成预览/编码/存储)。

下期预告:手把手实现一个 Raspberry Pi 上的 libcamera Pipeline Handler,从 sensor 注册到 stream 输出完整走通。


文章配图(搞笑图片,3:1宽高比):

程序员终于不用适配第37款SoC摄像头了

版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页