重构V4L2流程(解决传统read/write,采用内存映射mmap)
在用户空间申请内存对应内核空间缓存
MJPEG:运动静止图像压缩技术,原理:视频每一帧单独压缩成一张 JPG 图片连续播放,无帧间压缩;
req.memory = V4L2_MEMORY_MMAP
- 驱动在内核空间申请一组 DMA 物理连续帧缓存;
- 用户态调用
mmap(),把内核缓存直接映射到进程虚拟地址; - 摄像头硬件 DMA 直接写入共享内存,无需内核→用户态 CPU 拷贝,低延迟、高性能。
// 2. 使用mmap映射缓冲区到用户空间
// mmap参数说明:
// - NULL: 让系统自动选择映射地址
// - buf.length: 映射长度(缓冲区大小)
// - PROT_READ | PROT_WRITE: 可读可写权限
// - MAP_SHARED: 共享映射(与内核共享内存)
// - fd: 设备文件描述符
// - buf.m.offset: 缓冲区在设备文件中的偏移量
buffers[i].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, buf.m.offset);
DMA 缓冲区内存映射内核把摄像头硬件 DMA 物理内存,映射到用户进程虚拟地址空间,实现零拷贝采集
物理地址(硬件 / 驱动视角)DMA 缓冲区真实物理内存地址,只有内核、摄像头硬件能直接访问。
用户虚拟地址(你的程序指针 buffers [i].start)mmap 在你进程的虚拟地址空间分配一段虚拟地址,映射绑定到那块物理内存。
- MMAP 流程:open → S_FMT → REQBUFS → QUERYBUF + mmap → QBUF 全部缓存 → STREAMON → DQBUF 循环取帧
- READ 流程:open → S_FMT →直接 read 循环取帧,无缓存申请、映射、入队操作
#### 1.3.1 基本开发步骤
1. **检查设备**:确认 `/dev/video0` 存在且可访问
2. **打开设备**:使用 `open()` 打开设备文件
3. **查询能力**:使用 `VIDIOC_QUERYCAP` 获取设备信息
4. **枚举格式**:使用 `VIDIOC_ENUM_FMT` 查看支持的格式
5. **设置格式**:使用 `VIDIOC_S_FMT` 设置视频格式
6. **申请缓冲区**:使用 `VIDIOC_REQBUFS` 申请缓冲区
7. **映射缓冲区**:使用 `mmap()` 映射缓冲区
8. **启动采集**:使用 `VIDIOC_STREAMON` 开始采集
9. **循环采集**:使用 `VIDIOC_QBUF` 和 `VIDIOC_DQBUF` 循环获取帧
10. **停止采集**:使用 `VIDIOC_STREAMOFF` 停止采集
11. **清理资源**:释放缓冲区,关闭设备
