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:
三、核心流程图
3.1 libcamera 内部 Pipeline 数据流
从镜头光线到应用拿到帧数据,libcamera 内部经历如下管线:
3.2 V4L2 兼容层 vs Android HAL 路径对比
四、实操步骤
4.1 应用开发标准流程(5 步)
使用 libcamera C++ API 开发应用,标准流程分为 5 步:
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:
# 安装: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宽高比):