【Qt开发】V4L2 API详解 Camera详细设置

2022年01月13日 阅读数:8
这篇文章主要向大家介绍【Qt开发】V4L2 API详解 Camera详细设置,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
Camera的可设置项极多,V4L2 支持了很多。但Sam以前对这些设置的用法和涵义都是在看videodev2.h中边看边理解,感受很是生涩。直到写这篇blog时,才发现v4l2有专门的SPEC来讲明:
http://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html

但也基本没有时间仔细看了。先把本身看头文件看出的一些东西记录在这里吧。

以实际设置过程为顺序谈谈V4L2 设置。


1. 查询V4L2 功能集:VIDIOC_QUERYCAP
struct v4l2_capability cap;
int rel = 0;
ioctl(Handle, VIDIOC_QUERYCAP, &cap);

使用ioctl VIDIOC_QUERYCAP 来查询当前driver是否合乎规范。由于V4L2要求全部driver 和Device都支持这个Ioctl。 因此,能够经过这个ioctl是否成功来判断当前设备和dirver 是否支持V4L2规范。固然,这样同时还可以获得设备足够的能力信息。

struct v4l2_capability
{
 __u8 driver[16];   //驱动名。
 __u8 card[32];     // Device名
 __u8 bus_info[32];  //在Bus系统中存放位置
 __u32 version;      //driver 版本
 __u32 capabilities;  //能力集
 __u32 reserved[4];
};
能力集中包含:

V4L2_CAP_VIDEO_CAPTURE 0x00000001     The device supports the Video    Capture interface.php

V4L2_CAP_VIDEO_OUTPUT   0x00000002     The device supports the Video    Output interface.html

V4L2_CAP_VIDEO_OVERLAY 0x00000004     The device supports the Video    Overlay interface.linux

A video overlay device typically stores captured images directly in the video memory   of a graphics card,with hardware clipping and scaling.ide

V4L2_CAP_VBI_CAPTURE     0x00000010 The device supports the Raw  VBI Capture interface, providing Teletext and Closed Caption   data.spa

V4L2_CAP_VBI_OUTPUT     0x00000020      The device supports the Raw  VBI Output interface.nuxt

V4L2_CAP_SLICED_VBI_CAPTURE  0x00000040 The device supports the Sliced VBI Capture interface.code

V4L2_CAP_SLICED_VBI_OUTPUT   0x00000080 The device supports the Sliced VBI Output interface.orm

V4L2_CAP_RDS_CAPTURE    0x00000100          [to be defined]htm

#define V4L2_CAP_TUNER 0x00010000  
#define V4L2_CAP_AUDIO 0x00020000  
#define V4L2_CAP_RADIO 0x00040000  

#define V4L2_CAP_READWRITE 0x01000000  
#define V4L2_CAP_ASYNCIO 0x02000000  
#define V4L2_CAP_STREAMING 0x04000000  

看起来很熟悉吧,其实就是Driver里面的Type。


 __u8 driver[16]; driver名,一般为:uvcvideo
 __u8 card[32];  设备名:厂商会填写。
 __u8 bus_info[32];  bus,一般为:usb-hiusb-ehci-2.4
 __u32 version;
 __u32 capabilities;  一般为:V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
 __u32 reserved[4];



2. 枚举设备所支持的image format:  VIDIOC_ENUM_FMT
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(Handle, VIDIOC_ENUM_FMT, &fmtdesc);

使用ioctl VIDIOC_ENUM_FMT 依次询问,type为:V4L2_BUF_TYPE_VIDEO_CAPTURE。 index从0开始,依次增长,直到返回. Driver会填充结构体struct v4l2_fmtdesc的其它内容,若是index超出范围,则返回-1。
struct v4l2_fmtdesc
{
 __u32 index;                  // 须要填充,从0开始,依次上升。
 enum v4l2_buf_type type;      //Camera,则填写V4L2_BUF_TYPE_VIDEO_CAPTURE
 __u32 flags;                  // 若是压缩的,则Driver 填写:V4L2_FMT_FLAG_COMPRESSED,不然为0 
 __u8 description[32];         // image format的描述,如:YUV 4:2:2 (YUYV)
 __u32 pixelformat;        //所支持的格式。 如:V4L2_PIX_FMT_UYVY
 __u32 reserved[4];
}; 

这样,则知道当前硬件支持什么样的image format. 下一步,则能够设置image 了。固然,设置以前,还能够读取当前缺省设置。



3. 获得和设置Image Format: VIDIOC_G_FMT, VIDIOC_S_FMT:
3.1: 获得当前Image Format
struct v4l2_format Format;
memset(&Format, 0, sizeof(struct v4l2_format));
Format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(Handle, VIDIOC_G_FMT, &Format);

利用ioctl VIDIOC_G_FMT. 获得当前设置。
由于Camera为CAPTURE设备,因此须要设置type为: V4L2_BUF_TYPE_VIDEO_CAPTURE
而后Driver会填充其它内容。

struct v4l2_format
{
 enum v4l2_buf_type type;   // Camera,则用户必须填写:V4L2_BUF_TYPE_VIDEO_CAPTURE
 union
 {
 struct v4l2_pix_format pix;    // used by video capture and output devices
 struct v4l2_window win;
 struct v4l2_vbi_format vbi;
 struct v4l2_sliced_vbi_format sliced;
 __u8 raw_data[200];
 } fmt;
};

由于是Camera, 因此采用pix. 如今分析以下:
struct v4l2_pix_format
{
 __u32 width;    //Image width in pixels.
 __u32 height;   // Image Height in pixels.
 __u32 pixelformat;  // Image格式,最多见的有:V4L2_PIX_FMT_YYUV
 enum v4l2_field field; //是否逐行扫描,是否隔行扫描. Sam一般采用V4L2_FIELD_NONE,逐行放置数据 (注1)
 __u32 bytesperline; //每行的byte数
 __u32 sizeimage;     //总共的byte数,bytesperline * height
 enum v4l2_colorspace colorspace;  //This information supplements the pixelformat and must be set by the driver
 __u32 priv;
};


3.2:设置Image Format:VIDIOC_S_FMT
以前经过VIDIOC_ENUM_FMT已经知道Device支持什么Format。 因此就不用猜想了,直接设置吧。

设置Image  Format ,利用 iocto VIDIOC_S_FMT.
须要APPLICATION填写的Struct项目有:
struct v4l2_format Format;

Format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
Format.fmt.pix.width =  Width;
Format.fmt.pix.height = Height;
Format.fmt.pix.pixelformat= pixelformat;//V4L2_PIX_FMT_YUYV;
Format.fmt.pix.field = field;
io_rel = ioctl(Handle, VIDIOC_S_FMT, &Format);


SamInfo:以前设置了Image Format, 是指每一帧的数据格式,但Stream的行为呢,也须要设置,这就是下面所说的Stream 设置了。它就包含帧数设置和修改。



4. 获得和设置Stream信息:VIDIOC_G_PARM, VIDIOC_S_PARM
Stream信息,主要是设置帧数。
4.1:获得Stream信息:
struct v4l2_streamparm Stream_Parm;

memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));
Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

io_rel = ioctl(Handle, VIDIOC_G_PARM, &Stream_Parm);

用户只须要填充type为V4L2_BUF_TYPE_VIDEO_CAPTURE。 Driver就会把结构体中其它部分填充好。

struct v4l2_streamparm
{
 enum v4l2_buf_type type;
 union
 {
 struct v4l2_captureparm capture;
 struct v4l2_outputparm output;
 __u8 raw_data[200];
 } parm;
};

由于是Camera, 因此使用capture. 它是 struct v4l2_captureparm

struct v4l2_captureparm
{
 __u32 capability;    //是否能够被timeperframe控制帧数。能够则:V4L2_CAP_TIMEPERFRAME
 __u32 capturemode;   //是否为高清模式。若是是:
则设置为:V4L2_MODE_HIGHQUALITY。 高清模式会牺牲其它信息。一般设置为0。
 struct v4l2_fract timeperframe;  //帧数。
 __u32 extendedmode;  //定制的。若是不支持,设置为0
 __u32 readbuffers;
 __u32 reserved[4];
};


 struct v4l2_fract timeperframe;  //帧数。

struct v4l2_fract {
 __u32 numerator;  // 分子。 例:1
 __u32 denominator; //分母。 例:30
};

4.2:设置帧数:
struct v4l2_streamparm Stream_Parm;
memset(&Stream_Parm, 0, sizeof(struct v4l2_streamparm));
Stream_Parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 

Stream_Parm.parm.capture.timeperframe.denominator =Denominator;;
Stream_Parm.parm.capture.timeperframe.numerator = Numerator;

io_rel = ioctl(Handle, VIDIOC_S_PARM, &Stream_Parm);

请注意,哪怕ioctl返回0。也有可能没设置成功。因此须要再次Get。
固然,哪怕Get发现设置成功。真正抓帧也可能没那么高。



5. 利用VIDIOC_G_CTRL获得一些设置:
一些具体的设置,如曝光模式(Exposure Type),曝光值(Exposure),增益(Gain),白平衡(WHITE_BALANCE),亮度(BRIGHTNESS),饱和度(SATURATION),对比度(CONTRAST)等信息。能够经过VIDIOC_G_CTRL获得当前值。

用法:APP 填写结构体中的id. 经过调用VIDIOC_G_CTRL,driver 会填写结构体中value项。
struct v4l2_control ctrl;
struct v4l2_control
{
 __u32 id;
 __s32 value;
};



以曝光模式,曝光,和增益为例; 

曝光模式:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_EXPOSURE_AUTO;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
ctrl.value 则由Driver填写。告知当前曝光模式。
有如下几个选择:
enum  v4l2_exposure_auto_type {
V4L2_EXPOSURE_AUTO = 0,
V4L2_EXPOSURE_MANUAL = 1,
V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
V4L2_EXPOSURE_APERTURE_PRIORITY = 3
};


曝光:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
一样,driver填写ctrl.value. 内容为曝光值。

增益:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_GAIN;
ret = ioctl(Handle, VIDIOC_G_CTRL, &ctrl);
一样,driver填写ctrl.value. 内容为增益。



6. 利用VIDIOC_QUERYCTRL 获得设置具体信息:
在不少状况下,咱们并不知道如何设置一些信息,例如,曝光应该设置为多少?Driver可以接受的范围是多少?最大,最小值是多少?步长是多少?缺省值为多少?
能够经过VIDIOC_QUERYCTRL获得。

我们仍是以增益为例:
struct v4l2_queryctrl  Setting;
Setting.id = V4L2_CID_GAIN;
ret = ioctl(Handle, VIDIOC_QUERYCTRL, &Setting);
Driver就会填写结构体中全部信息。

struct v4l2_queryctrl
{
 __u32 id;  //用户设置。指定查找的是哪一个ID。
 enum v4l2_ctrl_type type;
 __u8 name[32];  //ID对应的名字。
 __s32 minimum;
 __s32 maximum;
 __s32 step;   //步长
 __s32 default_value;
 __u32 flags;
 __u32 reserved[2];
};
这样,就知道设置什么值是合法的了。那么,下一步就是设置了。


7. 利用VIDIOC_S_CTRL来设置:
很简单,设置id和value.调用ioctl就好。
仍是以增益为例:
struct v4l2_control ctrl;
ctrl.id = V4L2_CID_GAIN;
ctrl.value = Gain;
ret = ioctl(Handle, VIDIOC_S_CTRL, &ctrl);


有时候,硬件设置很奇怪,能够设置某个信息,却没法获得如何设置的信息。例如:HD-500能够设置增益。却没法获得该如何设置。



8. 利用扩展Ctrl设置:
焦距(FOUCE);

注1:enum v4l2_field field; 详解: