设备驱动-【转载】v4l2摄像头采集流程及应用程序

上一篇:v4l2 驱动-ioctl 调用流程

 https://www.cnblogs.com/zhangzhiwei122/p/16156730.html

 

原文链接:https://blog.csdn.net/liujun3512159/article/details/123857276

 

 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 }

 

posted @ 2022-04-17 18:43  张志伟122  阅读(268)  评论(0编辑  收藏  举报