Video4linux2(简称V4L2),是linux中关于视频设备的内核驱动。
V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移。更好地了解V4L2先从应用入手,然后再深入到内核中结合物理设备/接口的规范实现相应的驱动。V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioctl函数来实现。
在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。
操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。
v4l2一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。
read、write方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。
内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。
用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。
注意:
利用v4l2采集时,ARM上的采集卡支持3种模式,普通usb摄像头不支持直接读取模式;
采集卡支持V4L2_PIX_FMT_YUYV和V4L2_PIX_FMT_YUV420;
但是普通usb摄像头不支持V4L2_PIX_FMT_YUV420
captrue(直接读取方式):
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h> /* getopt_long() */
#include <fcntl.h> /* low-level i/o */
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h> /* for videodev2.h */
#include <linux/videodev2.h>
#define CODEC_NODE "/dev/video0"
#define LCD_WIDTH 320
#define LCD_HEIGHT 240
#define YUV_FRAME_BUFFER_SIZE (LCD_WIDTH*LCD_HEIGHT)+(LCD_WIDTH*LCD_HEIGHT)/2 /* YCBCR 420 */
static int cam_c_init(void);
static void exit_from_app() ;
static int cam_c_fp = -1;
static void exit_from_app()
{
close(cam_c_fp);
}
static int cam_c_init(void)
{
int dev_fp = -1;
dev_fp = open(CODEC_NODE, O_RDWR);
if (dev_fp < 0) {
perror(CODEC_NODE);
printf("CODEC : Open Failed \n");
return -1;
}
return dev_fp;
}
int main()
{
struct v4l2_capability cap;
struct v4l2_format codec_fmt;
/* Camera codec initialization */
if ((cam_c_fp = cam_c_init()) < 0)
exit_from_app();
/* Codec set */
/* Get capability */
int ret = ioctl(cam_c_fp , VIDIOC_QUERYCAP, &cap);
if (ret < 0) {
printf("V4L2 : ioctl on VIDIOC_QUERYCAP failled\n");
exit(1);
}
//printf("V4L2 : Name of the interface is %s\n", cap.driver);
/* Check the type - preview(OVERLAY) */
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
printf("V4L2 : Can not capture(V4L2_CAP_VIDEO_CAPTURE is false)\n");
exit(1);
}
/* Set format */
codec_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
codec_fmt.fmt.pix.width = LCD_WIDTH;
codec_fmt.fmt.pix.height = LCD_HEIGHT;
codec_fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;(The average captrue equiment can't support. )
ret = ioctl(cam_c_fp , VIDIOC_S_FMT, &codec_fmt);
if (ret < 0) {
printf("V4L2 : ioctl on VIDIOC_S_FMT failled\n");
exit(1);
}
int start=1;
ret = ioctl(cam_c_fp, VIDIOC_STREAMON, &start);
if (ret < 0) {
printf("V4L2 : ioctl on VIDIOC_STREAMON failed\n");
exit(1);
}
unsigned char yuv_buf[YUV_FRAME_BUFFER_SIZE];
char YUV_name[100];
static FILE *YUV_fp;
sprintf(&YUV_name[0], "Cam%dx%d-%d.yuv", LCD_WIDTH,