V4L2编程模型简介(一)
http://www.embedu.org/Column/Column514.htm
作者:邹南,华清远见嵌入式学院讲师。
简介:本文所附代码是根据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;
}
}
}
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;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
留言列表