嵌入式linux中有标准的V4L2协议,很多摄像头驱动和应用都是基于V4L2来进行的,一般情况下,摄像头的设备号为/dev/video0
#define DEVICE_NAME /dev/video0
一 、 打开设备
int fd;
void open_device(void)
{
fd = open(DEVICE_NAME, O_RDWR /*| O_NONBLOCK */, 0);
}
二 、 ioctl命令控制函数
void v4l2_ioctl(int fd, int request, void * arg)
{
int r;
r = ioctl (fd, request, arg);
}
三 、 参数初始化
int v4l2_setpara(void)
{
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_streamparm parm = {0};
struct v4l2_streamparm parm2 = {0};
struct v4l2_crop crop;
struct v4l2_format fmt;
unsigned int min;
if (-1 == v4l2_ioctl (fd, VIDIOC_QUERYCAP, &cap)) // 查询v4l2驱动功能
{
if (EINVAL == errno) //如果v4l2设备与内核驱动不兼容,则errno为EINVAL
{
printf("fd is no V4L2 device
");
return -1;
}
else return -2;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("fd is no video capture device
");
return -3;
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) //判断设备是否支持视频流方式
{
printf ("fd does not support streaming i/o
");
return -4;
}
/* Select video input, video standard and tune here. */
memset(&cropcap,0,sizeof(cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == v4l2_ioctl (fd, VIDIOC_CROPCAP, &cropcap)) // 查询裁剪能力,驱动会填充cropcap的其它成员
{
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; // 恢复成默认值
if (-1 == v4l2_ioctl (fd, VIDIOC_S_CROP, &crop)) //重新设置裁剪参数
{
switch (errno)
{
case EINVAL:
/* Cropping not supported. */
break;
default:
/* Errors ignored. */
break;
}
}
}
memset(&fmt,0,sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = v4l2cappara.width;
fmt.fmt.pix.height = v4l2cappara.height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; //这种图像格式两个像素占四个字节,其中每Y分量占两个字节,即一个像素。每Cb,Cr分别占一个字节
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (-1 == xioctl (v4l2cappara.v4l2fd, VIDIOC_S_FMT, &fmt)) //设置视频捕获格式
{
perror("VIDIOC_S_FMT:");
return -5;
}
/* Note VIDIOC_S_FMT may change width and height. */
/* Buggy driver paranoia. */
min = fmt.fmt.pix.width * 2; // 是16位 {MOD}的????????????????????
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;
/* parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(v4l2cappara.v4l2fd, VIDIOC_G_PARM, &parm) == -1)
{
printf("set frame rate failed
");
}
printf("frame time1: %d/%d capturemode = %d capability = %d
",parm.parm.capture.timeperframe.numerator
,parm.parm.capture.timeperframe.denominator,parm.parm.capture.capturemode,parm.parm.capture.capability);
parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
parm.parm.capture.timeperframe.numerator = 1001;
parm.parm.capture.timeperframe.denominator = 20000;
if (xioctl(v4l2cappara.v4l2fd, VIDIOC_S_PARM, &parm) == -1)
{
printf("set frame rate failed
");
}*/
return 1;
}
四 、 内存初始化
int v4l2_meminit(void)
{
struct v4l2_requestbuffers req;
int count,count_max;
memset(&req,0,sizeof(req));
req.count = 2;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == v4l2_ioctl (fd, VIDIOC_REQBUFS, &req)) // 向驱动申请分配缓冲区
{
if (EINVAL == errno)
{
printf ("fd does not support memory mapping
");
return -5;
}
else return -6;
}
if (req.count < 2)
{
printf ("Insufficient buffer memory on fd
");
return -7;
}
count_max = req.count;
if(req.count > V4L2BUFNUM)count_max = V4L2BUFNUM;
for (count = 0; count < count_max; ++count)
{
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = count;
if (-1 == v4l2_ioctl (fd, VIDIOC_QUERYBUF, &buf))
{
printf("fd VIDIOC_QUERYBUF error
"); // 得到物理内存位置
return -9;
}
v4l2cappara.v4l2capturebuf[count].length = buf.length;
v4l2cappara.v4l2capturebuf[count].start = mmap (NULL /* start anywhere */,buf.length, PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,v4l2cappara.v4l2fd, buf.m.offset); // 关联到用户空间
if (MAP_FAILED == v4l2cappara.v4l2capturebuf[count].start)
{
printf("encaputre fd init error mmap
");
return -10;
}
}
v4l2cappara.bufcount = count_max;
int i;
for (i = 0; i < v4l2cappara.bufcount; ++i)
{
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == v4l2_ioctl (fd, VIDIOC_QBUF, &buf)) //将申请到的帧缓冲入队列,以便存放采集到的数据
{
printf("encapture error VIDIOC_QBUF
");
return -1;
}
}
return 1;
}
五 、 开始获取视频
int v4l2_startcapturing(void)
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == v4l2_ioctl (fd, VIDIOC_STREAMON, &type)) //开始视频采集
{
printf("encapture error VIDIOC_STREAMON
");
return -2;
}
return 1;
}
六、 结束获取视频
void v4l2_stop_capturing(void)
{
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_ioctl(fd, VIDIOC_STREAMOFF, &type);
}