上一篇:v4l2 驱动-ioctl 调用流程
V4L2是linux上用于采集图片、视频、音频数据的一套框架,对上向应用程序提供统一的接口,对下支持各类复杂硬件的灵活扩展,
在远程会议、视频监控系统、嵌入式多媒体终端中都有广泛的应用。 V4L2通过打开驱动中创建的/dev/videoX设备,就可以进行一系列的操作,
比如申请内存、设置格式、设置属性、开启采集等等。
通过v4l2采集步骤
示例代码:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6 #include <linux/videodev2.h>
7 #include <string.h>
8 #include <sys/mman.h>
9
10
11 //用户层缓冲区保存结构体
12 typedef struct BUFTYPE
13 {
14 void *start;
15 int length;
16 }*usr_buf;
17 unsigned int buf_num = 4;//指定缓冲区个数
18
19 int fd;//打开的设备fd
20 /**
21 * @brief init_camera 初始化相机设备属性
22 * @param dev 设备名称
23 * @return 成功返回0,失败返回-1
24 */
25 int init_camera(const char* dev)
26 {
27 fd = open(dev, O_RDWR);
28 if(fd < 0){
29 printf("open \"%s\" error\n", dev);
30 return -1;
31 }
32
33 /**
34 * 查询设备属性
35 */
36 struct v4l2_capability cap;
37 int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
38 if (ret < 0) {
39 printf("VIDIOC_QUERYCAP error\n");
40 return -1;
41 }
42
43 printf("driver : %s\n",cap.driver);
44 printf("device : %s\n",cap.card);
45 printf("bus : %s\n",cap.bus_info);
46 printf("version : %d\n",cap.version);
47
48 if(cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE){ /*判断是否为视频捕获设备*/
49 if(cap.capabilities & V4L2_CAP_STREAMING){/*判断是否支持视频流捕获*/
50 printf("support capture\n");
51 }else{
52 printf("unsupport capture\n");
53 }
54 }else {
55 printf("error\n");
56 return -1;
57 }
58
59 struct v4l2_fmtdesc fmtdesc;
60 fmtdesc.index=0;
61 fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
62 while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
63 {
64 printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
65 fmtdesc.index++;
66 }
67
68 /*设置格式*/
69 struct v4l2_format fmt;
70 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头缓冲
71 fmt.fmt.pix.width = 640;
72 fmt.fmt.pix.height = 480;
73 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
74 if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0)
75 {
76 printf("set foramt:V4L2_PIX_FMT_MJPEG failed\n");
77 return -1;
78 }
79 return 0;
80 }
81
82
83 /**
84 * @brief mmap_buffer 分配用户缓冲区内存,并建立内存映射
85 * @return 成功返回0,失败返回-1
86 */
87 int mmap_buffer()
88 {
89 usr_buf = (BUFTYPE*)calloc(buf_num, sizeof(BUFTYPE));
90 if (!usr_buf) {
91 printf("calloc \"frame buffer\" error : Out of memory\n");
92 return -1;
93 }
94
95 struct v4l2_requestbuffers req;
96 req.count = buf_num; //帧缓冲数量
97 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //视频捕获缓冲区类型
98 req.memory = V4L2_MEMORY_MMAP; //内存映射方式
99 if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
100 printf("VIDIOC_REQBUFS fail\n");
101 return -1;
102 }
103
104
105 /*映射内核缓存区到用户空间缓冲区*/
106 for(unsigned int i = 0; i < buf_num; ++i)
107 {
108 /*查询内核缓冲区信息*/
109 struct v4l2_buffer v4l2_buf;
110 memset(&v4l2_buf, 0, sizeof(v4l2_buf));
111 v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
112 v4l2_buf.memory = V4L2_MEMORY_MMAP;
113 v4l2_buf.index = i;
114 if(ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf) < 0){
115 printf("VIDIOC_QUERYBUF failed\n");
116 return -1;
117 }
118
119 /* 建立映射关系
120 * 注意这里的索引号,v4l2_buf.index 与 usr_buf 的索引是一一对应的,
121 * 当我们将内核缓冲区出队时,可以通过查询内核缓冲区的索引来获取用户缓冲区的索引号,
122 * 进而能够知道应该在第几个用户缓冲区中取数据
123 */
124 usr_buf[i].length = v4l2_buf.length;
125 usr_buf[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset);
126
127 if (MAP_FAILED == usr_buf[i].start){//若映射失败,打印错误
128 printf("mmap failed: %d\n",i);
129 return -1;
130 }else{
131 if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0){ // 若映射成功则将内核缓冲区入队
132 printf("VIDIOC_QBUF failed\n");
133 return -1;
134 }
135 }
136 }
137 return 0;
138 }
139
140 /**
141 * @brief stream_on 开启视频流
142 * @return 成功返回0,失败返回-1
143 */
144 int stream_on()
145 {
146 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
147 if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
148 {
149 printf("VIDIOC_STREAMON failed\n");
150 return -1;
151 }
152 return 0;
153 }
154
155 /**
156 * @brief write_frame 读取一帧图像
157 * @return 返回图像帧的索引index,读取失败返回-1
158 */
159 int write_frame()
160 {
161 struct v4l2_buffer v4l2_buf;
162 v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
163 v4l2_buf.memory = V4L2_MEMORY_MMAP;
164 if(ioctl(fd, VIDIOC_DQBUF, &v4l2_buf) < 0) // 内核缓冲区出队列
165 {
166 printf("VIDIOC_DQBUF failed, dropped frame\n");
167 return -1;
168 }
169
170
171 /*
172 * 因为内核缓冲区与用户缓冲区建立的映射,所以可以通过用户空间缓冲区直接访问这个缓冲区的数据
173 */
174 char buffer[256];
175 sprintf(buffer,"/home/fox/qt_project/build-qt_cpp-Debug/%d.mjpg",v4l2_buf.index);
176 int file_fd = open(buffer,O_RDWR | O_CREAT); // 若打开失败则不存储该帧图像
177 if(file_fd > 0){
178 printf("saving %d images\n",v4l2_buf.index);
179 write(file_fd,usr_buf[v4l2_buf.index].start,v4l2_buf.bytesused);
180 close(file_fd);
181 }
182
183
184 if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0) //缓冲区重新入队
185 {
186 printf("VIDIOC_QBUF failed, dropped frame\n");
187 return -1;
188 }
189 return v4l2_buf.index;
190 }
191
192
193 /**
194 * @brief stream_off 关闭视频流
195 * @return 成功返回0,失败返回-1
196 */
197 int stream_off()
198 {
199 /*关闭视频流*/
200 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
201 if(ioctl(fd,VIDIOC_STREAMOFF,&type) == -1)
202 {
203 printf("Fail to ioctl 'VIDIOC_STREAMOFF'");
204 return -1;
205 }
206 return 0;
207 }
208
209 /**
210 * @brief unmap_buffer 解除缓冲区映射
211 * @return 成功返回0,失败返回-1
212 */
213 int unmap_buffer()
214 {
215 /*解除内核缓冲区到用户缓冲区的映射*/
216 for(unsigned int i = 0; i < buf_num; i++)
217 {
218 int ret = munmap(usr_buf[i].start, usr_buf[i].length);
219 if (ret < 0)
220 {
221 printf("munmap failed\n");
222 return -1;
223 }
224 }
225 free(usr_buf); // 释放用户缓冲区内存
226 return 0;
227 }
228
229 /**
230 * @brief release_camera 关闭设备
231 */
232 void release_camera()
233 {
234 close(fd);
235 }
236
237 int main(void)
238 {
239 int ret = init_camera("/dev/video1");
240 if(ret < 0){
241 printf("init_camera error\n");
242 return -1;
243 }
244
245 ret = mmap_buffer();
246 if(ret < 0){
247 printf("mmap_buffer error\n");
248 return -1;
249 }
250
251 ret = stream_on();
252 if(ret < 0){
253 printf("stream_on error\n");
254 return -1;
255 }
256
257 for(int i=0;i<5;i++)
258 {
259 write_frame();
260 }
261
262 ret = stream_off();
263 if(ret < 0){
264 printf("stream_off error\n");
265 return -1;
266 }
267
268 ret = unmap_buffer();
269 if(ret < 0){
270 printf("unmap_buffer error\n");
271 return -1;
272 }
273
274 release_camera();
275 return 0;
276 }