当前位置: 首页 > ds >正文

树莓派学习专题<10>:使用V4L2驱动获取摄像头数据--申请和管理缓冲区

树莓派学习专题<10>:使用V4L2驱动获取摄像头数据--申请和管理缓冲区

  • 1. 申请和管理缓冲区代码
  • 2. 代码解析
  • 3. 实测结果

1. 申请和管理缓冲区代码

/* 数据缓冲区 */
typedef struct tag_BufDesc
{void         *pvBufPtr ;size_t        szBufSize ;
} stBufDesc ;stBufDesc       *g_pstBufDesc ;/*********************************************** other codes * ********************************************//*******************************************************************************
- Function    : __AllocateBuffers
- Description : 本函数获取缓冲区,并将缓冲区映射到用户空间。
- Input       : VOID
- Output      : NULL
- Return      : VOID
- Others      :
*******************************************************************************/
void __AllocateBuffers(void)
{struct v4l2_requestbuffers stReqBuf ;struct v4l2_buffer         stBuf ;int                        iLoop ;memset(&stReqBuf, 0, sizeof(stReqBuf)) ;/* 申请缓冲区 */stReqBuf.count  = 4 ;stReqBuf.memory = V4L2_MEMORY_MMAP ;stReqBuf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE ;if(-1 == ioctl(g_iFDVideo, VIDIOC_REQBUFS, &stReqBuf)){printf("Request buffers failed.\n") ;exit(-1) ;}/* 申请用户空间缓冲区描述符空间 */g_pstBufDesc = (stBufDesc *)calloc(stReqBuf.count, sizeof(stBufDesc)) ;if(NULL == g_pstBufDesc){printf("Calloc buffer descriptor failed.\n") ;exit(-1) ;}/* 将申请的缓冲区映射到用户空间 */for(iLoop = 0 ; iLoop < stReqBuf.count ; iLoop++){memset(&stBuf, 0, sizeof(stBuf)) ;stBuf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;stBuf.index  = iLoop;stBuf.memory = V4L2_MEMORY_MMAP;if(-1 == ioctl(g_iFDVideo, VIDIOC_QUERYBUF, &stBuf)){printf("Get buffer information failed.\n") ;exit(-1) ;}g_pstBufDesc[iLoop].szBufSize = stBuf.length;g_pstBufDesc[iLoop].pvBufPtr  = mmap(NULL,stBuf.length,PROT_READ | PROT_WRITE,MAP_SHARED,g_iFDVideo,stBuf.m.offset);if(MAP_FAILED == g_pstBufDesc[iLoop].pvBufPtr){printf("Map V4L2 buffer to user space failed.\n") ;exit(-1) ;}if(-1 == ioctl(g_iFDVideo, VIDIOC_QBUF, &stBuf)){printf("Queue buffer failed.\n") ;exit(-1) ;}}printf("--Buffer information---------------------------------------\n") ;for(iLoop = 0 ; iLoop < stReqBuf.count ; iLoop++){printf("-- Frame buffer :%d   address :0x%x    size:%d\n", iLoop, g_pstBufDesc[iLoop].pvBufPtr, g_pstBufDesc[iLoop].szBufSize) ;}printf("-----------------------------------------------------------\n") ;return ;
}

2. 代码解析

命令 VIDIOC_REQBUFS 可申请视频帧缓冲。该命令的定义为:

#define VIDIOC_REQBUFS		_IOWR('V',  8, struct v4l2_requestbuffers)

可见该命令需要一个 struct v4l2_requestbuffers 类型的参数。该结构体类型定义为:

struct v4l2_requestbuffers {__u32			count;__u32			type;		/* enum v4l2_buf_type */__u32			memory;		/* enum v4l2_memory */__u32			capabilities;__u8			flags;__u8			reserved[3];
};

其中:

  1. count 填写需要申请多少块缓存。
  2. type 填写 V4L2_BUF_TYPE_VIDEO_CAPTURE
  3. memory 有如下四种选项。V4L2_MEMORY_MMAP 选项使用 memory mapping 型缓存,此时由驱动在内核空间开辟缓冲区,并将缓冲区告知应用程序;V4L2_MEMORY_USERPTR 选项,用户负责开辟缓冲区,并将缓冲区通告给驱动程序。V4L2_MEMORY_OVERLAY 尚未实现。V4L2_MEMORY_DMABUF 试用于两种设备之间做数据拷贝。MMAP 方式和 USERPTR 方式,都无需数据拷贝,由驱动直接将图像数据写入到缓冲。用户程序只需要管理和切换这些缓冲。
enum v4l2_memory {V4L2_MEMORY_MMAP             = 1,V4L2_MEMORY_USERPTR          = 2,V4L2_MEMORY_OVERLAY          = 3,V4L2_MEMORY_DMABUF           = 4,
};

VIDIOC_REQBUFS命令申请缓存,但是并没有返回申请缓存的地址。ioctl命令 VIDIOC_QUERYBUF 可以获取申请到的缓存的信息。该命令的定义如下:

#define VIDIOC_QUERYBUF		_IOWR('V',  9, struct v4l2_buffer)

其中,struct v4l2_buffer 的定义如下:

struct v4l2_buffer {__u32			index;__u32			type;__u32			bytesused;__u32			flags;__u32			field;struct timeval		timestamp;struct v4l2_timecode	timecode;__u32			sequence;/* memory location */__u32			memory;union {__u32           offset;unsigned long   userptr;struct v4l2_plane *planes;__s32		fd;} m;__u32			length;__u32			reserved2;union {__s32		request_fd;__u32		reserved;};
};

查询时,index 需要依次从0开始填写。例如上面代码中申请了4块缓存,则需要使用 VIDIOC_QUERYBUF 命令查询4次,index依次为0到3。type 字段和前述的相同,填写 V4L2_BUF_TYPE_VIDEO_CAPTUREmemory 字段填写 V4L2_MEMORY_MMAP,和前面设定的保持一致。

VIDIOC_QUERYBUF 命令执行后,需要关注offset 字段。该字段标志了实际分配的内存的位置。稍后mmap函数需要这个参数。

length 字段表示申请了多大的缓存。因为前面已经设定了输出的帧格式(长宽等),对于非压缩的输出格式,例如YUV420,YUYV,或者RGB格式,一帧画面需要多大的空间来保存,驱动会自己计算。这里会返回申请到的缓冲块大小。

最后,使用 mmap 函数,将内核空间的缓存和用户空间事先定义的缓存指针映射起来,这样用户空间就可以用缓存指针来操作这些缓存。

3. 实测结果

材料:

  1. Raspberry Pi 4B计算机;
  2. IMX219摄像头组件。
    运行上述代码,查看打印信息:
--Buffer information---------------------------------------
-- Frame buffer :0   address :0xf7b63000    size:1843200
-- Frame buffer :1   address :0xf79a1000    size:1843200
-- Frame buffer :2   address :0xf77df000    size:1843200
-- Frame buffer :3   address :0xf761d000    size:1843200
-----------------------------------------------------------

注意:因为我将摄像头输出的分辨率设定为了1280 X 720,输出格式为YUYV(每像素占用两字节),因此每帧画面需要的缓存大小为:

1280 * 720 * 2B = 1843200B

http://www.xdnf.cn/news/2673.html

相关文章:

  • 【PVR】《Adaptive Palm Vein Recognition Method》
  • codeforcesB. Binary Colouring
  • 实人认证开发指南:用API+深度学习构建人证合一系统
  • 【CF】Day45——Codeforces Round 1021 (Div. 2) BC
  • UV工具的安装与使用
  • 2025系统架构师---数据抽象(Data Abstraction)‌与‌面向对象架构风格
  • Android原生开发基础
  • 龙芯远程方案
  • 如何判断对一件事的认知深度?
  • Python+jieba文本分析示例:实现统计《红楼梦》中的人物并生成词云图
  • 人工智能——XGBoost 算法
  • 【2025最新Java面试八股】如何在Spring启动过程中做缓存预热?
  • 【基础篇】prometheus页面UI功能详解
  • AI翻译LangChain实现的一点有趣思考
  • 深入浅出提示词工程(结合 DeepSeek)
  • yolo-world踩坑指南
  • 服务器数据备份,服务器怎么备份数据呢?
  • 【Google Colab】利用unsloth针对医疗数据集进行大语言模型的快速微调(含跑通原代码)
  • 实现一个瀑布流布局
  • 文章记单词 | 第48篇(六级)
  • 【计算机组成原理实验】实验一 运算部件实验_加法器及计算机性能指标
  • 每日算法-250427
  • java异常
  • C++中的继承
  • 前端面试高频算法
  • 从增量式到绝对式 —— 深度理解编码器的原理与选型
  • 香港GPU显卡服务器与GPU云服务器的区别
  • linux blueZ 第六篇:嵌入式与工业级应用案例——在 Raspberry Pi、Yocto 与 Buildroot 上裁剪 BlueZ 并落地实战
  • 【遥感科普】不同波段的卫星影像分别有什么实际应用场景?
  • C语言内敛函数