一篇不错的v4l2


程序属这两篇文章最为经典了:

http://www.linuxidc.com/Linux/2011-03/33020.htm

http://www.linuxidc.com/Linux/2011-03/33021.htm


Linux系统中,视频设备被当作一个设备文件来看待,设备文件存放在 /dev目录下,完整路径的设备文件名为: /dev/video0 .


视频采集基本步骤流程如下: 打开视频设备,设置视频设备属性及采集方式、视频数据处理,关闭视频设备,如下图所示:


一、打开视频设备

打开视频设备非常简单,在V4L2中,视频设备被看做一个文件。使用open函数打开这个设备:


1.     用非阻塞模式打开摄像头设备
int cameraFd;
cameraFd = open("/dev/video0", O_RDWR | O_NONBLOCK);


2.     如果用阻塞模式打开摄像头设备,上述代码变为:
cameraFd = open("/dev/video0", O_RDWR);


关于阻塞模式和非阻塞模式

应用程序能够使用阻塞模式或非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。


 
二、Linux视频设备驱动常用控制命令使用说明
设置视频设备属性通过ioctl来进行设置,ioctl有三个参数,分别是fd, cmd,和parameter,表示设备描述符,控制命令和控制命令参数。


Linux 视频设备驱动接口V4L2支持的常用控制命令如下:


1. 控制命令 VIDIOC_ENUM_FMT      //ENUM什么意思?自己查查去


功能: 获取当前视频设备支持的视频格式 。


参数说明:参数类型为V4L2的视频格式描述符类型 struct v4l2_fmtdesc


返回值说明: 执行成功时,函数返回值为 0;struct v4l2_fmtdesc 结构体中的 .pixelformat和 .description 成员返回当前视频设备所支持的视频格式;


使用举例:


-------------------------------------------------------------------------------------------------


struct v4l2_fmtdesc fmt;


       memset(&fmt, 0, sizeof(fmt));


       fmt.index = 0;


       fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;


       while ((ret = ioctl(dev, VIDIOC_ENUM_FMT, &fmt)) == 0) {


              fmt.index++;


              printf("{ pixelformat = ''%c%c%c%c'', description = ''%s'' }\n",


                            fmt.pixelformat & 0xFF, (fmt.pixelformat >> 8) & 0xFF,


                            (fmt.pixelformat >> 16) & 0xFF, (fmt.pixelformat >> 24) & 0xFF,


                            fmt.description);


       }


-------------------------------------------------------------------------------------------------------

2. 控制命令VIDIOC_QUERYCAP        //query 和cap各代表什么意思


功能: 查询视频设备的功能 ;


参数说明:参数类型为V4L2的能力描述类型struct v4l2_capability ;


返回值说明: 执行成功时,函数返回值为 0;函数执行成功后,struct v4l2_capability 结构体变量中的返回当前视频设备所支持的功能;例如支持视频捕获功能V4L2_CAP_VIDEO_CAPTURE、V4L2_CAP_STREAMING等。


使用举例:

-------------------------------------------------------------------------------------------------------


struct v4l2_capability cap; 


iret = ioctl(fd_usbcam, VIDIOC_QUERYCAP, &cap);


if(iret < 0)


{


              printf("get vidieo capability error,error code: %d \n", errno);


              return ;


}


------------------------------------------------------------------------------------------------------


执行完VIDIOC_QUERYCAP命令后,cap变量中包含了该视频设备的能力信息,程序中通过检查cap中的设备能力信息来判断设备是否支持某项功能。
3. 控制命令VIDIOC_S_FMT           //直接告诉你,s是set的意思


功能: 设置视频设备的视频数据格式,例如设置视频图像数据的长、宽,图像格式(JPEG、YUYV格式);


参数说明:参数类型为V4L2的视频数据格式类型    struct v4l2_format  ;


返回值说明: 执行成功时,函数返回值为 0;


使用举例:

----------------------------------------------------------------------------------------------------------

       struct v4l2_format tv4l2_format; 


tv4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 


   tv4l2_format.fmt.pix.width = img_width; 


   tv4l2_format.fmt.pix.height = img_height; 


   tv4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; 


   tv4l2_format.fmt.pix.field = V4L2_FIELD_INTERLACED; 


   iret = ioctl(fd_usbcam, VIDIOC_S_FMT, &tv4l2_format);

-----------------------------------------------------------------------------------------------------------


注意:如果该视频设备驱动不支持你所设定的图像格式,视频驱动会重新修改struct v4l2_format结构体变量的值为该视频设备所支持的图像格式,所以在程序设计中,设定完所有的视频格式后,要获取实际的视频格式,要重新读取struct v4l2_format结构体变量。


 

4. 控制命令VIDIOC_REQBUFS            //我不问了,你自己问自己吧


功能: 请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存),V4L2是视频设备的驱动层,位于内核空间,所以通过VIDIOC_REQBUFS控制命令字申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。


参数说明:参数类型为V4L2的申请缓冲区数据结构体类型struct v4l2_requestbuffers  ;


返回值说明: 执行成功时,函数返回值为 0;V4L2驱动层分配好了视频缓冲区;


使用举例:


-----------------------------------------------------------------------------------------------------


struct v4l2_requestbuffers  tV4L2_reqbuf;


memset(&tV4L2_reqbuf, 0, sizeof(struct v4l2_requestbuffers ));


tV4L2_reqbuf.count = 1;    //申请缓冲区的个数


tV4L2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 


tV4L2_reqbuf.memory = V4L2_MEMORY_MMAP;

iret = ioctl(fd_usbcam, VIDIOC_REQBUFS, &tV4L2_reqbuf);


-----------------------------------------------------------------------------------------------------


注意:VIDIOC_REQBUFS会修改tV4L2_reqbuf的count值,tV4L2_reqbuf的count值返回实际申请成功的视频缓冲区数目;
5. 控制命令VIDIOC_QUERYBUF


功能: 查询已经分配的V4L2的视频缓冲区的相关信息,包括视频缓冲区的使用状态、在内核空间的偏移地址、缓冲区长度等。在应用程序设计中通过调VIDIOC_QUERYBUF来获取内核空间的视频缓冲区信息,然后调用函数mmap把内核空间地址映射到用户空间,这样应用程序才能够访问位于内核空间的视频缓冲区。


参数说明:参数类型为V4L2缓冲区数据结构类型    struct v4l2_buffer  ;


返回值说明: 执行成功时,函数返回值为 0;struct v4l2_buffer结构体变量中保存了指令的缓冲区的相关信息;


一般情况下,应用程序中调用VIDIOC_QUERYBUF取得了内核缓冲区信息后,紧接着调用mmap函数把内核空间地址映射到用户空间,方便用户空间应用程序的访问。


使用举例:


-------------------------------------------------------------------------------------------------------


struct v4l2_buffer tV4L2buf; 


memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));

tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 


tV4L2buf.memory = V4L2_MEMORY_MMAP; 


tV4L2buf.index = i;  // 要获取内核视频缓冲区的信息编号


iret = ioctl(fd_usbcam, VIDIOC_QUERYBUF, &tV4L2buf);


// 把内核空间缓冲区映射到用户空间缓冲区


AppBufLength  = tV4L2buf.length;


AppBufStartAddr = mmap(NULL /* start anywhere */ , 


                       tV4L2buf.length, 


                       PROT_READ | PROT_WRITE /* access privilege */ , 


                       MAP_SHARED /* recommended */ , 


                       fd_usbcam, tV4L2buf.m.offset); 


-------------------------------------------------------------------------------------------------------


上述代码在通过调用VIDIOC_QUERYBUF取得内核空间的缓冲区信息后,接着调用mmap函数把内核空间缓冲区映射到用户空间;关于mmap函数的用法,请读者查询相关资料;



6. 控制命令VIDIOC_QBUF


功能: 投放一个空的视频缓冲区到视频缓冲区输入队列中 ;


参数说明:参数类型为V4L2缓冲区数据结构类型    struct v4l2_buffer ;


返回值说明: 执行成功时,函数返回值为 0;函数执行成功后,指令(指定)的视频缓冲区进入视频输入队列,在启动视频设备拍摄图像时,相应的视频数据被保存到视频输入队列相应的视频缓冲区中。


使用举例:


-------------------------------------------------------------------------------------------------------


struct v4l2_buffer tV4L2buf; 


memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));

tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 


tV4L2buf.memory = V4L2_MEMORY_MMAP; 


tV4L2buf.index = i; //指令(指定)要投放到视频输入队列中的内核空间视频缓冲区的编号;

iret = ioctl(fd_usbcam, VIDIOC_QBUF, &tV4L2buf);


----------------------------------------------------------------------------------------------------
7. 控制命令VIDIOC_STREAMON


功能: 启动视频采集命令,应用程序调用VIDIOC_STREAMON启动视频采集命令后,视频设备驱动程序开始采集视频数据,并把采集到的视频数据保存到视频驱动的视频缓冲区中。


参数说明:参数类型为V4L2的视频缓冲区类型 enum v4l2_buf_type ;


返回值说明: 执行成功时,函数返回值为 0;函数执行成功后,视频设备驱动程序开始采集视频数据,此时应用程序一般通过调用select函数来判断一帧视频数据是否采集完成,当视频设备驱动完成一帧视频数据采集并保存到视频缓冲区中时,select函数返回,应用程序接着可以读取视频数据;否则select函数阻塞直到视频数据采集完成。Select函数的使用请读者参考相关资料。


使用举例:

----------------------------------------------------------------------------------------------------------

enum v4l2_buf_type v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 


fd_set    fds ; 


struct timeval   tv; 


iret = ioctl(fd_usbcam, VIDIOC_STREAMON, &v4l2type);


FD_ZERO(&fds); 


FD_SET(fd_usbcam,  &fds); 


tv.tv_sec = 2;       /* Timeout. */ 


tv.tv_usec = 0; 

iret = select(fd_usbcam+ 1, &fds, NULL, NULL, &tv); 


----------------------------------------------------------------------------------------------------------


8. 控制命令VIDIOC_DQBUF        //第二个D是删除的意思


功能: 从视频缓冲区的输出队列中取得一个已经保存有一帧视频数据的视频缓冲区;


参数说明:参数类型为V4L2缓冲区数据结构类型    struct v4l2_buffer ;


返回值说明: 执行成功时,函数返回值为 0;函数执行成功后,相应的内核视频缓冲区中保存有当前拍摄到的视频数据,应用程序可以通过访问用户空间来读取该视频数据。(前面已经通过调用函数mmap做了用户空间和内核空间的内存映射).


使用举例:

----------------------------------------------------------------------------------------------------------


struct v4l2_buffer tV4L2buf; 


memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));


tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 


tV4L2buf.memory = V4L2_MEMORY_MMAP; 


iret = ioctl(fd_usbcam, VIDIOC_DQBUF, &tV4L2buf);


Sasoritattoo注释:VIDIOC_DQBUF命令结果 使从队列删除的缓冲帧信息 传给了此tVL2buf。V4L2_buffer结构体的作用就相当于申请的缓冲帧的代理,找缓冲帧的都要先问问它,通过它来联系缓冲帧,起了中间桥梁的作用。


----------------------------------------------------------------------------------------------------------

9. 控制命令VIDIOC_STREAMOFF


功能: 停止视频采集命令,应用程序调用VIDIOC_ STREAMOFF停止视频采集命令后,视频设备驱动程序不在采集视频数据。


参数说明:参数类型为V4L2的视频缓冲区类型 enum v4l2_buf_type ;


返回值说明: 执行成功时,函数返回值为 0;函数执行成功后,视频设备停止采集视频数据。


使用举例:


----------------------------------------------------------------------------------------------------------

enum v4l2_buf_type  v4l2type; 


v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  


iret = ioctl(fd_usbcam, VIDIOC_STREAMOFF, &v4l2type);

-----------------------------------------------------------------------------------------------------------


以上就是Linux 视频设备驱动V4L2最常用的控制命令使用说明,通过使用以上控制命令,可以完成一幅视频数据的采集过程。

希望本文对大家理解linux下的视频驱动编程有所帮助。

猜你喜欢

转载自blog.csdn.net/xiaopang1122/article/details/72772405