选自《v4l2研究进展》 作者lapset
简介:本文所附代码是根据v4l2官方文档以及demo(capture.c)修改而来,纯粹为学习交流之用,请勿使用在商用场合。
地址:由于官方网的域名有敏感词汇,所以请google一下。
一 ,操作流程简单看
二 模块概要分析
以下是所附代码所涉及到的全局变量,摆出来只是参考,具体修改的话请自行安排。
- #define CLEAR(x) memset (&(x), 0, sizeof (x))
- typedef enum {
- #ifdef IO_READ
- IO_METHOD_READ,
- #endif
- #ifdef IO_MMAP
- IO_METHOD_MMAP,
- #endif
- #ifdef IO_USERPTR
- IO_METHOD_USERPTR,
- #endif
- } io_method;
- struct buffer {
- void * start;
- size_t length;
- };
- static io_method io = IO_METHOD_MMAP;
- static int fd = -1;
- struct buffer * buffers = NULL;
- static unsigned int n_buffers = 0;
- // global settings
- static unsigned int width = 640;
- static unsigned int height = 480;
- static unsigned char jpegQuality = 70;
- static char* jpegFilename = NULL;
- static char* deviceName = "/dev/video0";
1,deviceOpen
主要就是打开你的设备文件,一般情况下就是,/dev/vedio0 取决于你的设备数量。前面提到的stat这个结构体主要是记录了文件的基本信息。通过这一点来校验文件的打开权限。
2,deviceInit
这个模块稍微复杂些,它主要是使用了v4l2中定义的4种相关的数据结构。以下列出每种结构的具体属性。
- struct v4l2_cropcap {
- enum v4l2_buf_type type;
- struct v4l2_rect bounds;
- struct v4l2_rect defrect;
- struct v4l2_fract pixelaspect;
- };
- struct v4l2_crop {
- enum v4l2_buf_type type;
- struct v4l2_rect c;
- };
- struct v4l2_capability {
- __u8 driver[16]; /* i.e. "bttv" */
- __u8 card[32]; /* i.e. "Hauppauge WinTV"
*/ - __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev)
*/ - __u32 version; /* should use KERNEL_VERSION() */
- __u32 capabilities; /* Device capabilities */
- __u32 reserved[4];
- };
- struct v4l2_format {
- enum v4l2_buf_type type;
- union {
- struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
- struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
- struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
- struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
- __u8 raw_data[200]; /* user-defined */
- } fmt;
- };
这里不得不提醒一点,通常usb摄像头驱动,都会提供3种不同的数据传输方式,1,read IO 2,mmap内存映射 3,USERPTR(这一种是测试方法,具体可以去查询)
本文暂且只讨论常见的操作方法,即mmap内存映射方式.
通过一段时间的学习,才知道为什么只支持mmap,其实是我们所用的去架构是基于uvc.在uvc架构中,是不支持read/write io mode 以及用户扩展模式。
- static void deviceInit(void)
- {
- struct v4l2_capability cap;
- struct v4l2_cropcap cropcap;
- struct v4l2_crop crop;
- struct v4l2_format fmt;
- unsigned int min;
- if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { //get
the capab info about - if (EINVAL == errno) {
- fprintf(stderr, "%s
is no V4L2 device\n",deviceName); - exit(EXIT_FAILURE);
- } else {
- errno_exit("VIDIOC_QUERYCAP");
- }
- }
- if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { //check
is it support capture mode ? - fprintf(stderr, "%s
is no video capture device\n",deviceName); - exit(EXIT_FAILURE);
- }
- if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
- fprintf(stderr, "%s
does not support streaming i/o\n",deviceName); - exit(EXIT_FAILURE);
- }
- /* Select video input, video standard and tune here. */
- CLEAR(cropcap);// init -0 it is a
initialize func about set 0 to parameter - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
- crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- crop.c = cropcap.defrect; /*
reset to default */ - if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
- switch (errno) {
- case EINVAL:
- /* Cropping not supported. */
- break;
- default:
- /* Errors ignored. */
- break;
- }
- }
- } else {
- }
- CLEAR (fmt);
- // v4l2_format
- fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //mode is capture
- fmt.fmt.pix.width = width;
//define pixee width - fmt.fmt.pix.height = height;
//define pixel height - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
//define pixel format - fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
- if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
//set fmt - errno_exit("VIDIOC_S_FMT");
- /* Note VIDIOC_S_FMT may change width and height. */
- if (width != fmt.fmt.pix.width) {
- width = fmt.fmt.pix.width;
- fprintf(stderr,"Image
width set to %i by device %s.\n",width,deviceName); - }
- if (height != fmt.fmt.pix.height) {
- height = fmt.fmt.pix.height;
- fprintf(stderr,"Image
height set to %i by device %s.\n",height,deviceName); - }
- /* Buggy driver paranoia. */
- min = fmt.fmt.pix.width * 2;
- if (fmt.fmt.pix.bytesperline < min)
- fmt.fmt.pix.bytesperline = min;
- min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
- if (fmt.fmt.pix.sizeimage < min)
- fmt.fmt.pix.sizeimage = min;
- //this function is important to init mmap pre_work
- mmapInit();
- }
可以看到上面主要是初始化工作,具体的参数意义,请参看v4l2的specification 。
- static void mmapInit(void)
- {
- struct v4l2_requestbuffers req;//apply for frame buffer
- CLEAR (req);
- req.count = 4;
- req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- req.memory = V4L2_MEMORY_MMAP;
- if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
- if (EINVAL == errno) {
- fprintf(stderr, "%s
does not support memory mapping\n", deviceName); - exit(EXIT_FAILURE);
- } else {
- errno_exit("VIDIOC_REQBUFS");
- }
- }
- if (req.count < 2) {
- fprintf(stderr, "Insufficient
buffer memory on %s\n", deviceName); - exit(EXIT_FAILURE);
- }
- buffers = calloc(req.count, sizeof(*buffers));
- if (!buffers) {
- fprintf(stderr, "Out
of memory\n"); - exit(EXIT_FAILURE);
- }
- for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
- struct v4l2_buffer buf;
- CLEAR (buf);
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = n_buffers;
- if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
- errno_exit("VIDIOC_QUERYBUF");
- buffers[n_buffers].length = buf.length;
- buffers[n_buffers].start =
- mmap (NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /*
required */, MAP_SHARED /* recommended */, fd, buf.m.offset); - if (MAP_FAILED == buffers[n_buffers].start)
- errno_exit("mmap");
- }
- }
3,capture_start
初始化以后就可以进行正题了,就是所谓的capture data.不过在此之前,应该打开数据流通道,重点在于最后那个ioctl函数的参数:VIDIOC_STREAMON
- static void captureStart(void) //grap
after initialize - {
- unsigned int i;
- enum v4l2_buf_type type; //page-68
- #ifdef IO_MMAP
- for (i = 0; i < n_buffers; ++i) {
- struct v4l2_buffer buf;
- CLEAR (buf);
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = i;
- if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
- errno_exit("VIDIOC_QBUF");
- }
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
- errno_exit("VIDIOC_STREAMON");
- #endif
上面出现的两个结构体的分别定义如下:
- enum v4l2_buf_type {
- V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,
- V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,
- V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
- V4L2_BUF_TYPE_VBI_CAPTURE = 4,
- V4L2_BUF_TYPE_VBI_OUTPUT = 5,
- V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
- V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,
- #if 1
- /* Experimental */
- V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
- #endif
- V4L2_BUF_TYPE_PRIVATE = 0x80,
- };
- struct v4l2_buffer {
- __u32 index;
- enum v4l2_buf_type type;
- __u32 bytesused;
- __u32 flags;
- enum v4l2_field field;
- struct timeval timestamp;
- struct v4l2_timecode timecode;
- __u32 sequence;
- /* memory location */
- enum v4l2_memory memory;
- union {
- __u32 offset;
- unsigned long userptr;