【原创】IP摄像头技术纵览(二)—linux 视频开发接口V4L2概述
一、linux 视频开发接口V4L2概述
1. 什么是V4L2?
V4L2 全称 Video for Linux Two API Specification,它是Linux 内核中关于视频设备的子系统,它为linux 下的视频驱动提供了统一的接口,使得应用程序可以使用统一的API 函数操作不同的视频设备,极大地简化了视频系统的开发和维护。
由于早期的 V4L 有很多缺陷,Bill Dirks 等人对其进行了重新设计,并取名为Video for Linux Two,最早出现于Linux2.5.x 版本。V4L2 相比于V4L 有更好的扩展性和灵活性,并且支持的硬件设备更多。以下地址是几个非常全面的V4L2接口规范链接,文档详细讲解了V4L2相关知识和体系结构,地址如下:
(1)http://www.linuxtv.org/downloads/v4l-dvb-apis/index.html
(2)http://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html
(3)http://v4l.videotechnology.com/dwg/v4l2.pdf
2. V4L2支持设备
V4L2可以支持多种设备,它提供有以下几种接口:
(1)视频采集接口(video capture interface):这种应用的设备可以是高频头或者摄像头。V4L2的最初设计就是应用于这种功能的。
(2)视频输出接口(video output interface):可以驱动计算机的外围视频图像设备,可以输出视频到电视信号格式的设备。
(3) 直接传输视频接口(video overlay interface):它的主要工作是把从视频采集设备采集过来的信号直接输出到输出设备之上,而不用经过系统的CPU。
(4)视频间隔消隐信号接口(VBI interface):它可以使应用可以访问传输消隐期的视频信号。
(5)收音机接口(radio interface):可用来处理从AM或FM高频头设备接收来的音频流。
二、视频设备的一般操作流程
对V4L2 Device编程,通常包含以下步骤:
1、打开设备文件
fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);//打开设备
2、查询设备能力-capability
我更觉得应该是查询驱动能力,因为同一个USB摄像头,在电脑上与开发板上驱动输出的数据格式可能是不一样的。
int ioctl(intfd, int request, struct v4l2_capability *argp);
v4l2_capability 结构体定义如下:
structv4l2_capability
{
__u8 driver[16]; // 驱动名
__u8 card[32]; // 设备名
__u8 bus_info[32]; // 设备在系统总线中的信息
__u32 version; // 驱动版本号
__u32 capabilities; // 设备支持的操作--我们最关心的属性
__u32 reserved[4];
};
capabilities支持的定义如下:
V4L2_CAP_VIDEO_CAPTURE 0x00000001 //The device supports the Video Capture interface.
V4L2_CAP_VIDEO_OUTPUT 0x00000002 //The device supports the Video Output interface.
V4L2_CAP_VIDEO_OVERLAY 0x00000004 //The device supports the Video Overlay interface. A video overlay device typically stores captured images directly in the video memory of a graphics card, with hardware clipping and scaling.
V4L2_CAP_VBI_CAPTURE 0x00000010 //The device supports the Raw VBI Capture interface, providing Teletext and Closed Caption data.
V4L2_CAP_VBI_OUTPUT 0x00000020 //The device supports the Raw VBI Output interface.
V4L2_CAP_SLICED_VBI_CAPTURE 0x00000040 The device supports the Sliced VBI Capture interface.
V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 //The device supports the Sliced VBI Output interface.
V4L2_CAP_RDS_CAPTURE 0x00000100 //The device supports the RDS interface.
V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 //The device supports the Video Output Overlay (OSD) interface. Unlike the Video Overlay interface, this is a secondary function of video output devices and overlays an image onto an outgoing video signal. When the driver sets this flag, it must clear the V4L2_CAP_VIDEO_OVERLAY flag and vice versa.[a]
V4L2_CAP_HW_FREQ_SEEK 0x00000400 //The device supports the VIDIOC_S_HW_FREQ_SEEK ioctl for hardware frequency seeking.
V4L2_CAP_TUNER 0x00010000 //The device has some sort of tuner to receive RF-modulated video signals. For more information about tuner programming see Section 1.6, “Tuners and Modulators”.
V4L2_CAP_AUDIO 0x00020000 //The device has audio inputs or outputs. It may or may not support audio recording or playback, in PCM or compressed formats. PCM audio support must be implemented as ALSA or OSS interface. For more information on audio inputs and outputs see Section 1.5, “Audio Inputs and Outputs”.
V4L2_CAP_RADIO 0x00040000 //This is a radio receiver.
V4L2_CAP_MODULATOR 0x00080000 //The device has some sort of modulator to emit RF-modulated video/audio signals. For more information about modulator programming see Section 1.6, “Tuners and Modulators”.
V4L2_CAP_READWRITE 0x01000000 //The device supports the read() and/or write() I/O methods.
V4L2_CAP_ASYNCIO 0x02000000 //The device supports the asynchronous I/O methods.
V4L2_CAP_STREAMING 0x04000000 //The device supports the streaming I/O method.
其中我们最关心的是V4L2_CAP_VIDEO_CAPTURE 与 V4L2_CAP_STREAMING。
操作代码如下:
struct v4l2_capability cap;
if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
if (EINVAL == errno) {
fprintf (stderr, "%s is no V4L2 device/n",dev_name);
exit (EXIT_FAILURE);
} else {
errno_exit ("VIDIOC_QUERYCAP");
}
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf (stderr, "%s is no video capture device\n",dev_name);
exit (EXIT_FAILURE);
}
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
fprintf (stderr, "%s does not support streaming i/o\n",dev_name);
exit (EXIT_FAILURE);
}
3、格式查询
//查询驱动支持的数据格式
struct v4l2_format format;
memset(&format,0,sizeof(format));
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl(fd,VIDIOC_G_FMT,&format))
{
perror("VIDIOC_G_FMT(VIDEO_CAPTURE)");
return -1;
}
//struct v4l2_format数据结构定义如下:
struct v4l2_format
{
enum v4l2_buf_type type;
union
{
struct v4l2_pix_format pix;
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
__u8 raw_data[200];
} fmt;
};
//v4l2_buf_type type; 输入信息,让用户选择是哪种类型的设备。这里又与Driver中对应起来了。
enum v4l2_buf_type
{
V4L2_BUF_TYPE_VIDEO_CAPTURE
V4L2_BUF_TYPE_VIDEO_OUTPUT
V4L2_BUF_TYPE_VIDEO_OVERLAY
V4L2_BUF_TYPE_VBI_CAPTURE
V4L2_BUF_TYPE_VBI_OUTPUT
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE
V4L2_BUF_TYPE_SLICED_VBI_OUTPUT
V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY
V4L2_BUF_TYPE_PRIVATE
}
//最重要的是struct v4l2_pix_format pix; 这个也是新手最容出错的地方!!
struct v4l2_pix_format
{
__u32 width; //帧宽度
__u32 height; //桢高度
__u32 pixelformat; //像素格式。例如:V4L2_PIX_FMT_YUYV,V4L2_PIX_FMT_RGB332等。
enum v4l2_field field; //image包含逐行数据还是隔行数据。
__u32 bytesperline; //每行多少字节,通过width,height,pixelformat可以算出
__u32 sizeimage; //每桢多少字节。也可以算出。但需要加入field信息才能算出
enum v4l2_colorspace colorspace;
__u32 priv;
};
pixelformat—像素格式有十几种之多,设备驱动不可能支持输出所有的数据格式,而我们要将摄像头中的图像数据正确的解析就必须首先搞明白我的视频驱动支持输出什么格式!在我的开发板与ubuntu系统下,同一个usb摄像头输出的格式对比如下:
arm开发板上支持的pixelformat:
ubuntu下支持的pixelformat:
在我开始研究摄像头初期,看到网上很多人将摄像头数据读出后直接写入文件就能使用看图软件查看图像,而我按照他们的程序实验了好久都不行!最后走了相当多的弯路才发现,原来一切原因出自这里—pixelformat!! 此处暂时不展开讨论pixelformat,等文章最后再展开详细说明,这里只要记住这个字段很重要即可。
4、格式设置
通过格式查询我已经知道我的系统只支持YUYV4:2:2格式输出,所以,格式设置已经没什么好设置了,主要是设置图像宽度、高度信息等。
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 320;
fmt.fmt.pix.height = 240;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
errno_exit ("VIDIOC_S_FMT");
5、视频数据流读取
V4L2支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。前者一般用于连续视频数据的采集,后者常用于静态图片数据的采集。
- mmap方式,驱动将内部数据空间映射到应用程序空间上,双方直接在这个空间进行数据交换,是效率最高的方法,也是最常用的方式。
- 直接读取设备文件方式:调用read()、write()函数进行数据的读入和输出,该方法一般配合select()使用。