V4L是linux内核中关于视频设备的子系统,为linux下的视频驱动提供了统一的接口,使应用程序可以使用统一的API操作不同的视频设备,简化视频系统的开发与维护
V4L2相比与V4L有更好的扩展性和灵活性
(一)V4L2支持设备:
V4L2可以支持多种设备,可以有以下几种接口:
1)视频采集接口
2)视频输出接口
3)直接传输视频接口:将视频采集设备上采集的信号直接输出到视频输出设备上,不用经过系统CPU
4)视频间隔消隐信号接口(VBI Interface):使引用可以访问传输消隐期的视频信号
5)收音机接口:
(二)V4L2设备处理流程
打开V4L2设备节点
int
fd = open(
"
/dev/video0
"
,O_RDWR |O_NONBLOCK);
配置设备/查询设备属性
int
ioctl (
int
fd, unsigned
long
int
request, ...
/*
args
*/
) ;
常见的request命令:
VIDIOC_REQBUFS:在内核空间中分配帧缓冲区
struct
v4l2_requestbuffers req;
req.count
=
4
;
req.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory
=
V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_REQBUFS,
&req);
VIDIOC_QUERYBUF:将REQBUFS中分配的缓存转换成物理地址,并将物理地址映射到用户空间
for
(n_buffers =
0
; n_buffers < req.count; ++
n_buffers)
{
struct
v4l2_buffer buf;
memset(
&buf,
0
,
sizeof
(buf));
buf.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory
=
V4L2_MEMORY_MMAP;
buf.index
=
n_buffers;
if
(-
1
== ioctl(fd, VIDIOC_QUERYBUF, &
buf))
{
printf(
"
error in VIDIOC_QUERYBUF\n
"
);
return
-
1
;
}
buffers[n_buffers].length
=
buf.length;
buffers[n_buffers].start
=mmap (NULL,buf.length,PROT_READ |
PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);
if
(MAP_FAILED==
buffers[n_buffers].start)
return
-
1
;
}
VIDIOC_QUERYCAP:查询驱动功能
struct
v4l2_capability cap;
if
( ioctl(fd,VIDIOC_QUERYCAP,&cap) == -
1
)
printf(
"
error\n
"
);
printf(
"
capability:\n
"
);
printf(
"
driver:%s\n
"
,cap.driver);
printf(
"
card:%s\n
"
,cap.card);
printf(
"
bus info:%s\n
"
,cap.bus_info);
printf(
"
version:%d\n
"
,cap.version);
printf(
"
capabilities:%x\n
"
,cap.capabilities);
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
struct
v4l2_fmtdesc fmtdesc;
fmtdesc.index
=
0
;
fmtdesc.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf(
"
fmtdesc:\n
"
);
while
(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -
1
)
{
printf(
"
\t%d.%s\n
"
,fmtdesc.index+
1
,fmtdesc.description);
fmtdesc.index
++
;
}
VIDIOC_G/S_FMT:读取/设置当前驱动的视频捕捉格式
struct
v4l2_format format;
memset(
&format,
0
,
sizeof
(
struct
v4l2_format));
format.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
if
( ioctl(fd, VIDIOC_G_FMT, &format) == -
1
)
{
printf(
"
VIDIOC_G_FMT error\n
"
);
return
-
1
;
}
struct
v4l2_pix_format pix_format;
pix_format
=
format.fmt.pix;
printf(
"
pix_format\n
"
);
printf(
"
width:%d\n
"
,pix_format.width);
printf(
"
height:%d\n
"
,pix_format.height);
printf(
"
bytesperline:%d\n
"
,pix_format.bytesperline);
printf(
"
sizeimage:%d\n
"
,pix_format.sizeimage);
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_G/S_CROP:读取/设置视频信号的边框
struct
v4l2_cropcap cropcap;
struct
v4l2_crop crop;
cropcap.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
if
(
0
== ioctl(fd, VIDIOC_CROPCAP, &
cropcap))
{
crop.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c
=
cropcap.defrect;
if
(-
1
== ioctl(fd, VIDIOC_S_CROP, &
crop))
{
printf(
"
VIDIOC_S_CROP error\n
"
);
return
-
1
;
}
}
VIDIOC_QBUF:把缓存区放入缓存队列
VIDIOC_DQBUF:把缓存去从缓存队列中取出
unsigned
int
i;
enum
v4l2_buf_type type;
for
(i =
0
; i<
4
; ++
i)
{
struct
v4l2_buffer buf;
buf.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory
=
V4L2_MEMORY_MMAP;
buf.index
=
i;
ioctl (fd,VIDIOC_QBUF,
&
buf);
}
type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd,VIDIOC_STREAMON,
&
type);
struct
v4l2_buffer buf;
buf.type
=
V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory
=
V4L2_MEMORY_MMAP;
if
( ioctl (fd,VIDIOC_DQBUF, &buf)==-
1
)
{
printf(
"
error in VIDIOC_DQBUF\n
"
);
return
-
1
;
}
VIDIOC_STREAMON:开始视频显示函数
VIDIOC_STREAMOFF:结束视频显示函数
VIDIOC_QUERYSTD:检查当前视频设备支持的标准,亚洲一般使用PAL制式摄像头,欧洲一般使用NTSC摄像头
v4l2_std_id std;
int
ret;
do
{
ret
= ioctl(fd,VIDIOC_QUERYSTD,&
std);
}
while
(-
1
==ret && errno==
EAGAIN);
switch
(std)
{
case
V4L2_STD_NTSC:
//
case
V4L2_STD_PAL:
//
}
处理v4l2视频数据
v4l2设定了三种应用程序与驱动的交互方式:
1)直接读取设备文件方式read/write
2)mmap映射方式
3)用户指针方式
mmap方式:驱动将内部数据空间映射到应用程序空间上,双方直接在这个空间上进行数据交换
用户指针方式:首先由应用程序申请一段缓冲区,然后将缓冲区传给驱动,驱动将其作为缓冲区,从而实现内存共享
直接read/write:一般配合select使用,直接读取设备文件的方式进行I/O
关闭设备
调用close关闭文件描述符,如果进行了内存映射,关闭之前还需要munmap解除映射

