-
Notifications
You must be signed in to change notification settings - Fork 1
V4L2_Frame
SweerItTer edited this page Feb 1, 2026
·
3 revisions
Frame 是 utilsCore V4L2 模块的核心类,提供统一的帧数据接口,支持 MMAP 和 DMA-BUF 两种内存类型,自动管理缓冲区生命周期。
- 统一的帧数据接口
- 支持 MMAP 和 DMA-BUF 两种内存类型
- 自动管理缓冲区生命周期
- 提供元数据信息(时间戳、尺寸、索引等)
- 使用内存池优化分配性能
- V4L2 相机采集的帧数据封装
- 多线程间传递帧数据
- 零拷贝传输(配合 DMA-BUF)
- 帧数据的序列化和反序列化
- 依赖:
sharedBufferState(共享缓冲区状态管理) - 被依赖: VisionPipeline, RecordPipeline, RgaProcessor 等上层模块
Frame 是统一帧数据接口,提供:
- 单平面和多平面格式支持
- MMAP 和 DMA-BUF 两种内存类型
- RAII 自动生命周期管理
- 元数据信息封装
- 内存池优化分配性能
- RAII: 析构时自动归还缓冲区
- 内存池: 使用 FixedSizePool 减少 new/delete 开销
- 共享所有权: 使用 shared_ptr 管理缓冲区状态
FrameMeta 存储帧的元数据信息。
struct FrameMeta {
uint64_t frame_id = -1; // 单调递增帧ID
uint64_t timestamp_ns = -1; // 时间戳(CLOCK_MONOTONIC)
int index = -1; // V4L2缓冲区索引
uint32_t w = 0; // 原始宽度
uint32_t h = 0; // 原始高度
};| 字段 | 类型 | 说明 |
|---|---|---|
frame_id |
uint64_t |
单调递增的帧ID,用于标识帧序 |
timestamp_ns |
uint64_t |
时间戳(纳秒),使用 CLOCK_MONOTONIC
|
index |
int |
V4L2 缓冲区索引,用于归还缓冲区 |
w |
uint32_t |
图像宽度(像素) |
h |
uint32_t |
图像高度(像素) |
enum class MemoryType {
Unknown, // 未知类型
MMAP, // MMAP 内存
DMABUF // DMA-BUF 内存
};-
Unknown: 未知类型,未初始化的 Frame -
MMAP: MMAP 内存,通过 mmap 映射到用户空间 -
DMABUF: DMA-BUF 内存,直接使用 fd
Frame() noexcept;
Frame(SharedBufferPtr s); // 单平面
Frame(std::vector<SharedBufferPtr> states); // 多平面
~Frame();参数说明:
-
s(输入): 单平面缓冲区状态 -
states(输入): 多平面缓冲区状态列表
返回值: 无
所有权归属:
- Frame 持有 SharedBufferState 的所有权
- 通过
shared_ptr管理引用计数
注意事项:
- 默认构造函数创建空 Frame
- Frame 支持移动语义,但不支持拷贝(因为包含 std::function 成员)
- 使用内存池分配,提高性能(仅 Frame 类本身,派生类不会使用内存池)
static void* operator new(std::size_t size);
static void operator delete(void* p) noexcept;参数说明:
-
size(输入): 要分配的大小 -
p(输入): 要释放的指针
返回值:
-
operator new: 返回分配的内存指针 -
operator delete: 无返回值
所有权归属:
- 内存由 FixedSizePool 管理
注意事项:
- 重载了 operator new/delete 使用内存池
- 内存池大小: sizeof(Frame), 2048 个 block, 64 TLS cache, 32 page size
- 派生类不会使用内存池(size != sizeof(Frame) 时使用默认 new/delete)
- 不要手动调用 operator delete,由析构函数自动调用
MemoryType type() const noexcept;参数说明: 无
返回值:
-
MemoryType::MMAP: MMAP 内存 -
MemoryType::DMABUF: DMA-BUF 内存 -
MemoryType::Unknown: 未知类型
所有权归属: 只读访问
void* data(int planeIndex = -1) const;参数说明:
-
planeIndex(输入): 平面索引,-1 表示第一个平面
返回值:
- 成功: 返回数据指针
- 失败: 返回
nullptr
所有权归属:
- 返回的指针由 SharedBufferState 管理
- 不要手动释放
注意事项:
- 只在
MemoryType::MMAP模式下有效 - 多平面格式需要指定 planeIndex
- DMA-BUF 模式下应使用
dmabuf_fd() - 使用前会二次检查
valid标志 - 默认构造的 Frame(类型为 Unknown)调用此方法会返回错误并返回 nullptr
int dmabuf_fd(int planeIndex = -1) const;参数说明:
-
planeIndex(输入): 平面索引,-1 表示第一个平面(默认值)
返回值:
- 成功: 返回 DMA-BUF 文件描述符
- 失败: 返回 -1
所有权归属:
- 返回的 fd 由 SharedBufferState 管理
- Frame 析构时不会关闭 fd
注意事项:
- 只在
MemoryType::DMABUF模式下有效 - 多平面格式在 RK3568 上通常物理内存连续,只需 plane 0 的 fd
- fd 可以跨设备/进程共享
- 使用前会二次检查
valid标志 - 如果类型不是 DMABUF,会输出错误消息并返回 -1
使用例程:
// 获取 DMA-BUF fd
int fd = frame->dmabuf_fd();
if (fd >= 0) {
// 传递给其他硬件模块
rga_process(fd, width, height);
drm_display(fd, ...);
}size_t size() const;参数说明: 无
返回值: 缓冲区的总字节数
所有权归属: 只读访问
注意事项:
- 多平面 Frame 返回所有平面的总大小
- 单平面 Frame 返回单个平面的大小
SharedBufferPtr sharedState(int planeIndex = -1) const noexcept;参数说明:
-
planeIndex(输入): 平面索引,-1 表示第一个平面
返回值: SharedBufferPtr 智能指针
所有权归属: Frame 持有引用计数,返回引用
注意事项:
- 返回的
shared_ptr可以跨线程共享 - 使用前检查
valid标志 - 不要手动管理 SharedBufferState 的生命周期
- 多平面格式需要指定 planeIndex
- planeIndex 越界时返回 nullptr
实现细节:
SharedBufferPtr sharedState(int planeIndex = -1) const noexcept {
if (!mutiPlane_) {
return state_;
}
if (planeIndex < 0 || planeIndex >= states_.size()) {
fprintf(stderr, "Frame is mutiplane.\n");
return nullptr;
}
auto s = states_[planeIndex];
if (nullptr == s || false == s->valid) {
fprintf(stderr, "Current Plane is invalid.\n");
return nullptr;
}
return s;
}int index() const { return meta.index; }参数说明: 无
返回值: V4L2 缓冲区索引
所有权归属: 只读访问
uint64_t timestamp() const { return meta.timestamp_ns; }参数说明: 无
返回值: 时间戳(纳秒)
所有权归属: 只读访问
void setTimestamp(uint64_t ts);参数说明:
-
ts(输入): 时间戳(纳秒)
返回值: 无
所有权归属: 无
void setReleaseCallback(std::function<void(int)> bufReleasCallback);参数说明:
-
bufReleasCallback(输入): 释放回调函数
返回值: 无
所有权归属: Frame 持有回调函数的所有权
注意事项:
- 通常由 CameraController 自动设置
- Frame 析构时会自动调用此回调
- 用于归还缓冲区到 V4L2 队列
Frame::~Frame() {
if (bufReleasCallback_ && state_ != nullptr) {
if (meta.index >= 0)
bufReleasCallback_(meta.index); // 自动归还缓冲区
}
}创建流:
CameraController → FramePtr(std::move(sharedState)) → 回调接收者持有
使用流:
回调接收者持有 FramePtr → 访问数据 → 使用完毕后 Frame 析构
归还流:
Frame 析构 → bufReleasCallback_(meta.index) → CameraController::returnBuffer(index)
-
引用计数:
shared_ptr本身是线程安全的 -
valid 标志:
atomic<bool>保证跨线程可见性 - 析构回调: 在析构线程中执行,确保缓冲区归还
- 二次检查: data() 和 dmabuf_fd() 会二次检查 valid 标志
static FixedSizePool s_pool_(sizeof(Frame), 2048, 64, 32);- Block 大小: sizeof(Frame)
- 总 block 数: 2048
- TLS 缓存: 64
- Page 大小: 32
- 减少 new/delete 系统调用
- 线程本地缓存(TLS)减少锁竞争
- 预分配内存避免动态分配开销
camera.setFrameCallback([](FramePtr frame) {
if (frame->type() == Frame::MemoryType::MMAP) {
void* data = frame->data();
size_t size = frame->size();
// 访问数据
memcpy(process_buffer, data, size);
}
});camera.setFrameCallback([](FramePtr frame) {
if (frame->type() == Frame::MemoryType::DMABUF) {
int fd = frame->dmabuf_fd();
uint32_t w = frame->meta.w;
uint32_t h = frame->meta.h;
// 零拷贝传递给 RGA
rga_process(fd, w, h);
// Frame 析构时自动归还缓冲区
}
});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); // FramePtr 拷贝到队列
});
// 消费线程
FramePtr frame = frame_queue.front();
frame_queue.pop();
// 使用 frame
auto state = frame->sharedState();
if (state && state->valid) {
// 处理数据
}
// Frame 析构时自动归还缓冲区camera.setFrameCallback([](FramePtr frame) {
if (frame->type() == Frame::MemoryType::DMABUF) {
// NV12 格式通常只需要 plane 0 的 fd
int fd = frame->dmabuf_fd(0);
// Y 平面
uint8_t* y_data = ...; // 从 fd 映射或直接使用
// UV 平面(通过偏移量计算)
uint8_t* uv_data = y_data + (frame->meta.w * frame->meta.h);
}
});- 内存池: Frame 使用 FixedSizePool 分配,不要手动 delete
- 多平面访问: 访问多平面格式时,planeIndex 必须有效
-
valid 检查: 访问数据前检查
sharedState()->valid标志 -
拷贝和移动: Frame 未实现拷贝/移动构造和对应运算符重载, 但
std::function<void(int)>为不可拷贝对象, 因此建议使用shared_ptr管理 - 自动归还: Frame 析构时会自动调用释放回调
- 线程安全: 多个线程不能同时访问同一个 Frame 的数据
- 二次检查: data() 和 dmabuf_fd() 会二次检查 valid 标志
- DMA-BUF 连续性: 多平面格式在 RK3568 上通常物理内存连续
- 派生类: 派生类不会使用内存池(size != sizeof(Frame))
- CameraController - 摄像头控制器
- FormatTool - 格式工具
- SharedBufferState - 共享缓冲区状态
- FixedSizePool - 固定大小内存池
- 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 监视器