在Linux系统中,libcamera是替代传统V4L2框架的新一代摄像头管理库,提供了更现代化的API设计,支持多摄像头管理、格式协商、流控制等高级功能。开发者使用C++结合libcamera API可以高效完成摄像头相关的功能开发,适配各类嵌入式设备和桌面Linux环境。

环境准备
在开始编程前,需要先安装libcamera的开发依赖。不同Linux发行版的安装命令略有差异,以Ubuntu为例,执行以下命令安装所需组件:
# 安装libcamera核心库和开发文件 sudo apt update sudo apt install libcamera-dev libcamera0 # 安装编译工具 sudo apt install g++ cmake
libcamera核心概念
使用libcamera API前需要了解几个核心类的作用:
- CameraManager:摄像头管理器,负责枚举系统中的所有摄像头设备,是获取摄像头实例的入口。
- Camera:代表一个具体的摄像头设备,用于配置设备参数、申请流资源。
- Stream:摄像头的数据流,对应一种图像输出格式,一个摄像头可以支持多个流。
- FrameBuffer:存储图像数据的缓冲区,采集到的图像数据会写入到该缓冲区中。
- Request:摄像头请求对象,用于将缓冲区提交给摄像头进行数据采集。
基础编程流程
1. 初始化摄像头管理器
首先需要创建CameraManager实例,并枚举系统中的摄像头设备,选择需要使用的摄像头。
#include <libcamera/camera_manager.h>
#include <libcamera/camera.h>
#include <iostream>
#include <memory>
int main() {
// 创建摄像头管理器
std::unique_ptr<libcamera::CameraManager> cm = std::make_unique<libcamera::CameraManager>();
// 初始化管理器,枚举所有摄像头
int ret = cm->start();
if (ret < 0) {
std::cerr << "摄像头管理器初始化失败" << std::endl;
return -1;
}
// 获取摄像头列表
std::vector<std::shared_ptr<libcamera::Camera>> cameras = cm->cameras();
if (cameras.empty()) {
std::cerr << "未检测到可用摄像头" << std::endl;
cm->stop();
return -1;
}
// 选择第一个摄像头
std::shared_ptr<libcamera::Camera> camera = cameras[0];
std::cout << "使用摄像头: " << camera->id() << std::endl;
return 0;
}
2. 配置摄像头参数
获取到摄像头实例后,需要配置摄像头的输出流参数,比如分辨率、像素格式等。
#include <libcamera/camera_manager.h>
#include <libcamera/camera.h>
#include <libcamera/stream.h>
#include <libcamera/controls.h>
#include <iostream>
#include <memory>
#include <vector>
int main() {
std::unique_ptr<libcamera::CameraManager> cm = std::make_unique<libcamera::CameraManager>();
int ret = cm->start();
if (ret < 0) {
std::cerr << "摄像头管理器初始化失败" << std::endl;
return -1;
}
std::vector<std::shared_ptr<libcamera::Camera>> cameras = cm->cameras();
if (cameras.empty()) {
std::cerr << "未检测到可用摄像头" << std::endl;
cm->stop();
return -1;
}
std::shared_ptr<libcamera::Camera> camera = cameras[0];
// 获取摄像头的可用流配置
std::unique_ptr<libcamera::CameraConfiguration> config = camera->generateConfiguration({ libcamera::StreamRole::Viewfinder });
if (!config || config->empty()) {
std::cerr << "无法生成摄像头配置" << std::endl;
cm->stop();
return -1;
}
// 设置流的分辨率和像素格式
libcamera::StreamConfiguration& streamConfig = config->at(0);
streamConfig.size = { 1280, 720 }; // 设置分辨率为1280x720
streamConfig.pixelFormat = libcamera::formats::YUYV; // 设置像素格式为YUYV
// 验证并应用配置
ret = config->validate();
if (ret < 0) {
std::cerr << "摄像头配置验证失败" << std::endl;
cm->stop();
return -1;
}
ret = camera->configure(config.get());
if (ret < 0) {
std::cerr << "摄像头配置应用失败" << std::endl;
cm->stop();
return -1;
}
std::cout << "摄像头配置完成" << std::endl;
cm->stop();
return 0;
}
3. 分配缓冲区并采集图像
配置完成后需要为流分配FrameBuffer,然后提交请求采集图像数据。
#include <libcamera/camera_manager.h>
#include <libcamera/camera.h>
#include <libcamera/stream.h>
#include <libcamera/framebuffer.h>
#include <libcamera/request.h>
#include <iostream>
#include <memory>
#include <vector>
#include <unistd.h>
// 请求完成回调函数
void requestComplete(libcamera::Request* request) {
if (request->status() == libcamera::Request::RequestComplete) {
std::cout << "图像采集完成" << std::endl;
// 这里可以处理缓冲区中的图像数据
const libcamera::FrameBuffer* buffer = request->buffers().begin()->second;
std::cout << "缓冲区数据长度: " << buffer->planes()[0].length << std::endl;
}
delete request;
}
int main() {
std::unique_ptr<libcamera::CameraManager> cm = std::make_unique<libcamera::CameraManager>();
int ret = cm->start();
if (ret < 0) {
std::cerr << "摄像头管理器初始化失败" << std::endl;
return -1;
}
std::vector<std::shared_ptr<libcamera::Camera>> cameras = cm->cameras();
if (cameras.empty()) {
std::cerr << "未检测到可用摄像头" << std::endl;
cm->stop();
return -1;
}
std::shared_ptr<libcamera::Camera> camera = cameras[0];
// 配置摄像头
std::unique_ptr<libcamera::CameraConfiguration> config = camera->generateConfiguration({ libcamera::StreamRole::Viewfinder });
libcamera::StreamConfiguration& streamConfig = config->at(0);
streamConfig.size = { 1280, 720 };
streamConfig.pixelFormat = libcamera::formats::YUYV;
config->validate();
camera->configure(config.get());
// 获取配置后的流
libcamera::Stream* stream = config->at(0).stream();
// 分配缓冲区,这里简化分配逻辑,实际需要根据流的缓冲区需求分配
std::vector<std::unique_ptr<libcamera::FrameBuffer>> buffers;
// 注册请求完成回调
camera->requestCompleted.connect(requestComplete);
// 启动摄像头
ret = camera->start();
if (ret < 0) {
std::cerr << "摄像头启动失败" << std::endl;
cm->stop();
return -1;
}
// 创建请求并提交缓冲区
libcamera::Request* request = camera->createRequest();
// 这里需要将分配的缓冲区绑定到请求中,简化示例省略具体缓冲区分配逻辑
// request->addBuffer(stream, buffer.get());
ret = camera->queueRequest(request);
if (ret < 0) {
std::cerr << "请求提交失败" << std::endl;
camera->stop();
cm->stop();
return -1;
}
// 等待采集完成,实际开发中可以使用事件循环
sleep(2);
// 停止摄像头
camera->stop();
cm->stop();
return 0;
}
编译与运行
编写好代码后,使用g++编译时需要链接libcamera库,编译命令如下:
g++ -o camera_test camera_test.cpp `pkg-config --cflags --libs libcamera`
运行生成的可执行文件即可完成基础的摄像头采集测试,如果系统中有可用摄像头,会输出对应的摄像头ID和配置信息。
注意事项
- libcamera需要内核支持相关的媒体控制器框架,部分老旧内核可能需要升级才能正常使用。
- 不同摄像头的可用分辨率和像素格式不同,需要先通过API查询设备支持的能力再做配置。
- 实际开发中缓冲区分配需要按照libcamera的缓冲区分配规则进行,避免内存访问错误。
- 如果需要持续采集图像,需要在请求完成后重新提交新的请求,形成采集循环。