-
Notifications
You must be signed in to change notification settings - Fork 1
V4L2_CameraController
SweerItTer edited this page Feb 1, 2026
·
3 revisions
CameraController 是 utilsCore V4L2 模块的核心类,提供 V4L2 摄像头设备的高级封装,负责相机设备的初始化、配置、采集控制和缓冲区管理。
- V4L2 设备打开和初始化
- 摄像头参数配置(分辨率、格式、帧率等)
- 缓冲区管理(MMAP 和 DMA-BUF 两种模式)
- 异步帧采集和回调机制
- 线程控制和 CPU 亲和性设置
- V4L2 摄像头的实时采集
- 视频流的获取和处理
- 零拷贝数据传输(配合 DMA-BUF)
- 多线程视频处理流水线
- 依赖:
v4l2内核子系统 - 被依赖: VisionPipeline, RecordPipeline 等上层模块
CameraController 是 V4L2 摄像头的完整封装,提供:
- PIMPL 模式隐藏实现细节
- MMAP 和 DMA-BUF 两种内存模式
- 异步帧采集回调机制
- 双缓冲区管理
- 线程控制功能
- PIMPL 模式: 使用 Impl 类隐藏实现细节
- 回调机制: 通过 FrameCallback 异步通知帧数据
- RAII: 自动管理设备资源和缓冲区
Config 结构体用于配置 CameraController 的运行参数。
struct Config {
int buffer_count = 4; // 缓冲区数量
__u32 plane_count = 2; // 平面数量(多平面格式)
bool use_dmabuf = false; // 内存类型:true=DMA-BUF, false=MMAP
std::string device = "/dev/video0"; // 设备路径
uint32_t width = 1280; // 图像宽度
uint32_t height = 720; // 图像高度
uint32_t format = V4L2_PIX_FMT_NV12; // 像素格式
};| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
buffer_count |
int |
4 | V4L2 缓冲区数量,建议 4-6 |
plane_count |
__u32 |
2 | 多平面格式的平面数(NV12=2, RGB=1) |
use_dmabuf |
bool |
false | true=使用 DMA-BUF, false=使用 MMAP |
device |
string |
"/dev/video0" | V4L2 设备路径 |
width |
uint32_t |
1280 | 图像宽度(像素) |
height |
uint32_t |
720 | 图像高度(像素) |
format |
uint32_t |
NV12 |
像素格式(V4L2_PIX_FMT_*) |
explicit CameraController(const Config& config);
~CameraController();参数说明:
-
config(输入): 配置结构体
返回值: 无
所有权归属: CameraController 拥有设备 fd 和所有缓冲区的所有权
注意事项:
- 构造时会自动打开设备和初始化
- 失败时会抛出异常
- 必须先设置回调才能调用
start()
void start();参数说明: 无
返回值: 无
所有权归属: 无
注意事项:
- 必须先调用
setFrameCallback()设置回调 - 启动采集线程,开始获取帧数据
- 失败时会抛出异常
使用例程:
CameraController::Config cfg;
cfg.device = "/dev/video0";
cfg.width = 1920;
cfg.height = 1080;
cfg.format = V4L2_PIX_FMT_NV12;
cfg.use_dmabuf = true;
CameraController camera(cfg);
camera.setFrameCallback([](FramePtr frame) {
printf("Got frame: %dx%d\n", frame->meta.w, frame->meta.h);
});
camera.start(); // 开始采集void pause();参数说明: 无
返回值: 无
所有权归属: 无
注意事项:
- 暂停采集,但保持设备打开状态
- 不会释放缓冲区
- 可以通过
stop()停止采集 - 暂停期间不会触发帧回调
void stop();参数说明: 无
返回值: 无
所有权归属: 无
注意事项:
- 停止采集线程
- 释放所有缓冲区
- 关闭设备 fd
- 调用后需要重新创建 CameraController 才能再次使用
void setThreadAffinity(int cpu_core);参数说明:
-
cpu_core(输入): CPU 核心编号
返回值: 无
所有权归属: 无
注意事项:
- 绑定采集线程到指定 CPU 核心
- 提高缓存命中率,减少上下文切换
- 必须在
start()之前调用
void returnBuffer(int index);参数说明:
-
index(输入): 缓冲区索引
返回值: 无
所有权归属: 无
注意事项:
- 通常由 Frame 析构函数自动调用
- 手动调用可以提前归还缓冲区
- 索引必须在有效范围内
- 线程安全,可以并发调用
void setFrameCallback(FrameCallback&& callback);参数说明:
-
callback(输入): 帧回调函数,类型为std::function<void(FramePtr)>
返回值: 无
所有权归属: CameraController 持有回调函数的所有权
注意事项:
- 必须先调用此方法再调用
start() - 回调函数会在采集线程中调用
- 不要在回调中执行耗时操作
- 建议使用队列传递帧数据到其他线程
使用例程:
// 简单回调
camera.setFrameCallback([](FramePtr frame) {
// 快速处理
printf("Frame: %dx%d\n", frame->meta.w, frame->meta.h);
});
// 使用队列传递
std::queue<FramePtr> frame_queue;
std::mutex queue_mutex;
camera.setFrameCallback([&](FramePtr frame) {
std::lock_guard<std::mutex> lock(queue_mutex);
frame_queue.push(frame);
});int getDeviceFd() const;参数说明: 无
返回值: V4L2 设备文件描述符
所有权归属: 只读访问,不要手动关闭
注意事项:
- fd 由 CameraController 管理
- 可以用于直接调用 V4L2 ioctl
- 返回的 fd 由 CameraController 负责关闭
FrameCallback 是采集线程通知用户有新帧数据的回调机制。
- 每次成功采集到一帧数据时
- 在采集线程中调用
- 异步通知,不阻塞采集流程
- FramePtr 的所有权通过
std::move转移给回调接收者 - 回调接收者负责持有 FramePtr
- Frame 析构时会自动归还缓冲区到 V4L2 队列
- 回调在采集线程中调用
- 多个 FramePtr 不能共享,每个 Frame 有独立的所有权
- 需要额外的同步机制来管理回调中的数据流
DMA-BUF 模式下,CameraController 分配的缓冲区可以直接被其他硬件模块(RGA、DRM、MPP)使用,实现零拷贝传输。
1. 打开设备 (/dev/video0)
2. 查询设备能力(是否支持 DMA-BUF)
3. 分配 DMA-BUF (DmaBuffer::create)
4. 将 DMA-BUF fd 关联到 V4L2 缓冲区
5. 启动流
V4L2 采集 → DMA-BUF fd → FramePtr (回调中) → 用户持有
- V4L2 不直接拥有 DMA-BUF
- CameraController 拥有 DMA-BUF 的 DRM handle
- FramePtr 通过 SharedBufferState 持有 DMA-BUF fd
- Frame 析构时自动归还缓冲区
| 资源 | 创建者 | 拥有者 | 释放方式 | 线程保护 |
|---|---|---|---|---|
| V4L2 fd | CameraController | CameraController |
~CameraController() → close()
|
内部同步 |
| V4L2 缓冲区 | CameraController | CameraController |
stop() → VIDIOC_STREAMOFF
|
内部同步 |
| DMA-BUF handle | CameraController | CameraController | ~DmaBuffer() |
fd_mutex |
| DMA-BUF fd | CameraController | SharedBufferState | ~SharedBufferState() |
原子标志 |
| FramePtr | CameraController | 回调接收者 |
~Frame() → returnBuffer()
|
自动归还 |
创建流:
CameraController 构造 → 拥有 V4L2 fd 和缓冲区
采集流:
V4L2 采集 → 创建 FramePtr → 回调中 std::move → 回调接收者持有
归还流:
Frame 构构 → bufReleasCallback_() → returnBuffer(index) → 归还到 V4L2 队列
-
缓冲区访问: 使用
std::mutex _mutex_保护缓冲区队列操作 -
线程状态: 使用
std::atomic<bool>控制运行/停止/暂停状态 -
Frame 所有权: 通过
std::move转移所有权,避免并发访问
- 回调函数: 不要在回调中阻塞,建议使用队列传递
- Frame 使用: 每个 FramePtr 只能由一个线程持有
-
停止等待:
stop()会等待采集线程退出 -
暂停恢复:
pause()是线程安全的
// 1. 配置参数
CameraController::Config cfg;
cfg.device = "/dev/video0";
cfg.width = 1280;
cfg.height = 720;
cfg.format = V4L2_PIX_FMT_NV12;
cfg.use_dmabuf = true;
// 2. 创建控制器
CameraController camera(cfg);
// 3. 设置回调
camera.setFrameCallback([](FramePtr frame) {
printf("Frame: %dx%d, timestamp=%lu\n",
frame->meta.w, frame->meta.h, frame->meta.timestamp_ns);
});
// 4. 启动采集
camera.start();
// 5. 运行一段时间
sleep(10);
// 6. 停止采集
camera.stop();std::queue<FramePtr> frame_queue;
std::mutex queue_mutex;
std::condition_variable queue_cv;
camera.setFrameCallback([&](FramePtr frame) {
{
std::lock_guard<std::mutex> lock(queue_mutex);
frame_queue.push(frame);
}
queue_cv.notify_one();
});
// 消费线程
std::thread consumer([&]() {
while (running) {
FramePtr frame;
{
std::unique_lock<std::mutex> lock(queue_mutex);
queue_cv.wait(lock, [&] { return !frame_queue.empty() || !running; });
if (!running) break;
frame = frame_queue.front();
frame_queue.pop();
}
// 处理帧
if (frame) {
process_frame(frame);
}
}
});CameraController::Config cfg;
// ... 配置 ...
CameraController camera(cfg);
// 绑定到 CPU 核心 1
camera.setThreadAffinity(1);
camera.setFrameCallback(...);
camera.start();-
必须先设置回调:
setFrameCallback()必须在start()之前调用 - 回调中不要阻塞: 建议使用队列传递帧数据到其他线程
-
不要手动归还缓冲区: Frame 析构时会自动调用
returnBuffer() - DMA-BUF 连续性: 多平面格式通常物理内存连续,只需 plane 0 的 fd
-
停止前等待: 调用
stop()前确保没有异步操作在执行 - 线程安全: 多线程使用时,使用队列和互斥锁保护共享数据
- 设备独占: CameraController 独占设备,不能同时打开同一个设备
- 格式支持: 不同摄像头支持的格式不同,需要查询设备能力
-
暂停功能: 使用
pause()暂停采集,使用stop()停止采集 - 不可拷贝: CameraController 禁止拷贝和移动
- Frame - 帧数据接口
- FormatTool - V4L2 格式工具
- SharedBufferState - 共享缓冲区状态
- V4L2 模块总览
主页
API 文档
DMA 模块
DRM 模块
- DRM 模块总览
- DeviceController - DRM 设备控制器
- DrmLayer - DRM 图层管理
- PlanesCompositor - DRM 平面合成器
- DrmBpp - DRM 格式定义
NET 模块
- NET 模块总览
- TcpServer - TCP 服务器
- SocketConnection - Socket 连接管理
- CommandHandler - 命令处理器
- DataPacket - 数据包
V4L2 模块
- V4L2 模块总览
- CameraController - V4L2 摄像头控制器
- Frame - V4L2 帧数据结构
- FormatTool - V4L2 格式工具
- Exception - V4L2 异常类
V4L2Param 模块
- V4L2Param 模块总览
- ParamControl - 参数控制
- ParamLogger - 参数日志
- ParamProcessor - 参数处理器
RGA 模块
- RGA 模块总览
- RgaConverter - RGA 转换器
- RgaProcessor - RGA 处理器
- FormatTool - RGA 格式工具
MPP 模块
- MPP 模块总览
- EncoderContext - 编码器上下文
- EncoderCore - 编码器核心
- JpegEncoder - JPEG 编码器
- StreamWriter - 流写入器
- MppResourceGuard - MPP 资源守护
- FileTools - 文件工具
- FormatTool - 格式工具
Sys 模块
- Sys 模块总览
- CpuMonitor - CPU 监控器
- MemoryMonitor - 内存监控器
- Base - 基础类
Mouse 模块
- Mouse 模块总览
- Watcher - 鼠标监视器
Utils 模块
- Utils 模块总览
- AsyncThreadPool - 异步线程池
- ConcurrentQueue - 并发队列
- FdWrapper - 文件描述符包装器
- FenceWatcher - 围栏监视器
- FixedSizePool - 固定大小对象池
- Logger - 日志记录器
- ObjectsPool - 对象池
- OrderedQueue - 有序队列
- ProgressBar - 进度条
- SafeQueue - 安全队列
- SharedBufferState - 共享缓冲区状态
- SimpleVariant - 简单变体类型
- ThreadPauser - 线程暂停器
- ThreadUtils - 线程工具
- Types - 类型定义
- UdevMonitor - Udev 监视器