V4L2+Qt5实现摄像头视频采集以及参数控制

这一段时间在做摄像头控制方面的工作,需要在Linux下实现对摄像头名称和分辨率的获取,同时对亮度、对比度、曝光值等参数进行控制,同时还需要对获取的帧画面进行处理。目前除了图像处理方面,简单的使用V4l2获取设备属性并可以打开摄像头进行参数控制,以及将读取的原始YUYV2帧数据转换为RGB24格式显示在QLabel上都可以实现,今天先在这里做个总结。

1.对于V4l2常用的结构体以及相关的命令符,网上都有很多参考,对照着官网文档慢慢理解也都能顺下来,下边也就放一点自己学习期间的理解,可能还有错误的地方,不过也没时间细看了,放着以后有时间再来完善吧。

-----------------------------------------------------------------------V4L2常用结构体说明----------------------------------------------------------------------
结构体来源:/usr/include/linux/videodev2.h文件。
*************************************************************************************************************************************************
struct v4l2_capability {
    __u8    driver[16];            //驱动名称
    __u8    card[32];            //设备名称
    __u8    bus_info[32];        //总线信息
    __u32   version;            //驱动版本号
    __u32    capabilities;        //设备具备的功能
    __u32    device_caps;        //通过特定设备(节点)访问的功能(不知道用处,网上其它资料没有该字段)
    __u32    reserved[3];        //保留字段
};
说明:该结构体常用来获取设备信息,使用VIDIOC_QUERYCAP命令符,包括驱动名、设备名以及版本号等等,其中capabilities成员表示设备支持的操作模式,比如V4L2_CAP_VIDEO_CAPTURE代表着该设备是一个视频捕捉设备,可以通过(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)来判定该设备是否具备改能力。由于V4L2涵盖了各种设备,不只是摄像头设备,不同的设备具备的能力也不一定相同,因此有需要时也可以来判定指定设备的能力。
/* Values for 'capabilities' field */
#define V4L2_CAP_VIDEO_CAPTURE        0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT        0x00000002  /* Is a video output device */
#define V4L2_CAP_VIDEO_OVERLAY        0x00000004  /* Can do video overlay */
#define V4L2_CAP_VBI_CAPTURE        0x00000010  /* Is a raw VBI capture device */
#define V4L2_CAP_VBI_OUTPUT        0x00000020  /* Is a raw VBI output device */
#define V4L2_CAP_SLICED_VBI_CAPTURE    0x00000040  /* Is a sliced VBI capture device */
#define V4L2_CAP_SLICED_VBI_OUTPUT    0x00000080  /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE        0x00000100  /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY    0x00000200  /* Can do video output overlay */
#define V4L2_CAP_HW_FREQ_SEEK        0x00000400  /* Can do hardware frequency seek  */
#define V4L2_CAP_RDS_OUTPUT        0x00000800  /* Is an RDS encoder */

/* Is a video capture device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_CAPTURE_MPLANE    0x00001000
/* Is a video output device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_OUTPUT_MPLANE    0x00002000
/* Is a video mem-to-mem device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_M2M_MPLANE    0x00004000
/* Is a video mem-to-mem device */
#define V4L2_CAP_VIDEO_M2M        0x00008000

#define V4L2_CAP_TUNER            0x00010000  /* has a tuner */
#define V4L2_CAP_AUDIO            0x00020000  /* has audio support */
#define V4L2_CAP_RADIO            0x00040000  /* is a radio device */
#define V4L2_CAP_MODULATOR        0x00080000  /* has a modulator */

#define V4L2_CAP_SDR_CAPTURE        0x00100000  /* Is a SDR capture device */
#define V4L2_CAP_EXT_PIX_FORMAT        0x00200000  /* Supports the extended pixel format */
#define V4L2_CAP_SDR_OUTPUT        0x00400000  /* Is a SDR output device */

#define V4L2_CAP_READWRITE              0x01000000  /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */

#define V4L2_CAP_DEVICE_CAPS            0x80000000  /* sets device capabilities field */
*************************************************************************************************************************************************
struct v4l2_fmtdesc {
    __u32            index;             /* Format number      */
    __u32            type;              /* enum v4l2_buf_type */
    __u32               flags;
    __u8            description[32];   /* Description string */
    __u32            pixelformat;       /* Format fourcc      */
    __u32            reserved[4];
};
说明:该结构体是用来获取设备支持的图像格式,通过VIDIOC_ENUM_FMT命令符;index为格式的序号(可以从0开始循环列举),由程序设置;type只能设置为V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY这几个;flags为是否压缩;description保存着对格式的描述,如"YUV 4:2:2";pixelformat使用v4l2_fourcc()宏保存图像格式,#define v4l2_fourcc(a,b,c,d) (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)),可以使用    printf("%c%c%c%c\n",fmtdesc.pixelformat&0XFF,(fmtdesc.pixelformat>>8)&0XFF,(fmtdesc.pixelformat>>16)&0XFF,(fmtdesc.pixelformat>>24)&0XFF);来查看格式;reserved保留位,驱动开发时必须要设置为0;
*************************************************************************************************************************************************
struct v4l2_frmsizeenum {
    __u32            index;        /* Frame size number */
    __u32            pixel_format;    /* Pixel format */
    __u32            type;        /* Frame size type the device supports. */

    union {                    /* Frame size */
        struct v4l2_frmsize_discrete    discrete;
        struct v4l2_frmsize_stepwise    stepwise;
    };

    __u32   reserved[2];            /* Reserved space for future use */
};
struct v4l2_frmsize_discrete {
    __u32            width;        /* Frame width [pixel] */
    __u32            height;        /* Frame height [pixel] */
};

struct v4l2_frmsize_stepwise {
    __u32            min_width;    /* Minimum frame width [pixel] */
    __u32            max_width;    /* Maximum frame width [pixel] */
    __u32            step_width;    /* Frame width step size [pixel] */
    __u32            min_height;    /* Minimum frame height [pixel] */
    __u32            max_height;    /* Maximum frame height [pixel] */
    __u32            step_height;    /* Frame height step size [pixel] */
};
说明:该结构体使用VIDIOC_ENUM_FRAMESIZES命令来遍历获取设备分辨率。index为用户指定的索引值(最好从0开始);pixel_format需要指定为v4l2_fmtdesc结构体的pixelformat(v4l2_fmtdesc结构体需要先获取一下);type类型只有三种:V4L2_FRMSIZE_TYPE_DISCRETE(UVC设备驱动固定为该类型)、V4L2_FRMSIZE_TYPE_CONTINUOUS以及V4L2_FRMSIZE_TYPE_STEPWISE,具体有什么区别不清楚,不过使用时不需要指定type;discrete和stepwise结构体保存着帧大小的宽和高相关,一般使用discrete来获取分辨率宽和高即可;reserved为保留位;
*************************************************************************************************************************************************
struct v4l2_format {
    __u32     type;
    union {
        struct v4l2_pix_format        pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
        struct v4l2_pix_format_mplane    pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
        struct v4l2_window        win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
        struct v4l2_vbi_format        vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
        struct v4l2_sliced_vbi_format    sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
        struct v4l2_sdr_format        sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
        __u8    raw_data[200];                   /* user-defined */
    } fmt;
};
说明:该结构体可以使用VIDIOC_G_FMT命令符获取图像格式(如宽和高,YUYV等),使用VIDIOC_S_FMT设置图像格式。type为枚举类型v4l2_buf_type,camera设备需要设置为V4L2_BUF_TYPE_VIDEO_CAPTURE类型;。
*************************************************************************************************************************************************
struct v4l2_pix_format {
    __u32             width;            //图像宽
    __u32            height;            //图像高
    __u32            pixelformat;    //图像颜色编码格式,如YUYV
    __u32            field;        /* enum v4l2_field */
    __u32                bytesperline;    /* for padding, zero if unused */
    __u32                  sizeimage;
    __u32            colorspace;    /* enum v4l2_colorspace */
    __u32            priv;        /* private data, depends on pixelformat */
    __u32            flags;        /* format flags (V4L2_PIX_FMT_FLAG_*) */
    __u32            ycbcr_enc;    /* enum v4l2_ycbcr_encoding */
    __u32            quantization;    /* enum v4l2_quantization */
    __u32            xfer_func;    /* enum v4l2_xfer_func */
};
说明:存储图像信息格式的结构体;field为v4l2_field枚举类型,决定视频扫描图像的顺序,是逐行还是隔行或是其它,具体用处不清楚,用户设置时可能无效;bytesperline文档相邻行最左边像素的距离,但是实际是指每行的字节数;sizeimage图像大小,计算方式为bytesperline*height;colorspace由驱动设定,如V4L2_COLORSPACE_JPEG(7);
*************************************************************************************************************************************************
struct v4l2_cropcap {
    __u32            type;    /* enum v4l2_buf_type */
    struct v4l2_rect        bounds;
    struct v4l2_rect        defrect;
    struct v4l2_fract       pixelaspect;
};
说明:该结构体使用VIDIOC_CROPCAP命令符查询图像裁切功能。type类型由应用程序设置,只有V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY,和自定义(驱动程序定义)类型的代码V4L2_BUF_TYPE_PRIVATE 及更高版本是有效的;bounds参数为摄像头能捕捉到的窗口大小限制;defrect参数为裁剪大小,默认裁剪整个窗口,可有v4l2_crop结构体来获取或设置当前裁剪窗口大小;pixelaspect参数为宽高比例,驱动里默认为1/1;
*************************************************************************************************************************************************
struct v4l2_crop {
    __u32            type;    /* enum v4l2_buf_type */
    struct v4l2_rect        c;
};
说明:该结构体用来获取或设置图像裁剪矩形区域的值。有些设备不支持裁剪设置,可以通过if(ret == -1 && errno != EINVAL) perror("VIDIOC_S_CROP");来进行判断设备该能力。
*************************************************************************************************************************************************
struct v4l2_queryctrl {
    __u32             id;
    __u32             type;    /* enum v4l2_ctrl_type */
    __u8             name[32];    /* Whatever */
    __s32             minimum;    /* Note signedness */
    __s32             maximum;
    __s32             step;
    __s32             default_value;
    __u32            flags;
    __u32             reserved[2];
};
说明:该结构体是用(VIDIOC_QUERYCTRL, VIDIOC_QUERYMENU)来查询某个id对应的具体参数的;比如查询亮度值可以设置id = V4L2_CID_BRIGHTNESS。在Control IDs列表中第一项为V4L2_CID_BASE,最后一项为V4L2_CID_LASTP1(其实最后为V4L2_CID_PRIVATE_BASE项,不过该项为自定义项),可以通过这两个临界值去遍历该设备所有具备的控制能力。
常用id值:
V4L2_CID_CONTRAST               (V4L2_CID_BASE+1)            /* 对比度调节 */
V4L2_CID_SATURATION             (V4L2_CID_BASE+2)            /* 饱和度调节 */
V4L2_CID_AUDIO_VOLUME           (V4L2_CID_BASE+5)            /* 音量调节 */
V4L2_CID_AUDIO_MUTE             (V4L2_CID_BASE+9)            /* 静音设置 */
V4L2_CID_DO_WHITE_BALANCE       (V4L2_CID_BASE+13)           /* 白平衡调节 */
V4L2_CID_GAMMA                  (V4L2_CID_BASE+16)           /* 伽马值调节 */
V4L2_CID_EXPOSURE               (V4L2_CID_BASE+17)           /* 曝光度调节 */
V4L2_CID_PRIVATE_ATXX_FLASH     (V4L2_CID_PRIVATE_BASE + 2)  /* 闪光灯控制 */
V4L2_CID_PRIVATE_ATXX_FRAME     (V4L2_CID_PRIVATE_BASE + 12) /* 帧率调节 */
type类型:
enum v4l2_ctrl_type {
    V4L2_CTRL_TYPE_INTEGER         = 1,        //整型
    V4L2_CTRL_TYPE_BOOLEAN         = 2,        //布尔型,0代表禁用,1代表启用;
    V4L2_CTRL_TYPE_MENU              = 3,        //菜单,需要VIDIOC_QUERYMENU命令符;
    V4L2_CTRL_TYPE_BUTTON         = 4,        //无值
    V4L2_CTRL_TYPE_INTEGER64     = 5,        //64位整型
    V4L2_CTRL_TYPE_CTRL_CLASS    = 6,        
    V4L2_CTRL_TYPE_STRING        = 7,        
    V4L2_CTRL_TYPE_BITMASK       = 8,        
    V4L2_CTRL_TYPE_INTEGER_MENU  = 9,        

    /* Compound types are >= 0x0100 */
    V4L2_CTRL_COMPOUND_TYPES     = 0x0100,    
    V4L2_CTRL_TYPE_U8         = 0x0100,        
    V4L2_CTRL_TYPE_U16         = 0x0101,        
    V4L2_CTRL_TYPE_U32         = 0x0102,        
};
flags标志:
V4L2_CTRL_FLAG_DISABLED    0×0001    此控件将永久禁用,应由应用程序忽略。任何更改控件的尝试都将导致EINVAL错误代码。
V4L2_CTRL_FLAG_GRABBED    0×0002 该控制暂时不可更改,例如因为另一个应用程序接管了对相应资源的控制。这些控件可以特别在用户界面中显示。尝试更改控件可能会导致 EBUSY错误代码。
V4L2_CTRL_FLAG_READ_ONLY    0x0004    此控件仅可永久读取。任何更改控件的尝试都将导致EINVAL错误代码。
V4L2_CTRL_FLAG_UPDATE    ×0008    提示更改此控件可能会影响同一控件类中其他控件的值。应用程序应相应地更新其用户界面。
V4L2_CTRL_FLAG_INACTIVE    0×0010    此控件不适用于当前配置,应在用户界面中相应显示。例如,当用另一个控制选择MPEG音频编码等级1时,可以在MPEG音频等级2比特率控制上设置标志。
V4L2_CTRL_FLAG_SLIDER    0×0020    提示此控件最好表示为用户界面中类似滑块的元素。
V4L2_CTRL_FLAG_WRITE_ONLY    即0x0040    此控件仅可永久写入。任何读取控件的尝试都将导致EACCES错误代码错误代码。该标志通常存在于相对控制或动作控制中,其中写入值将使设备执行给定动作(例如电动机控制),但是不能返回有意义的值。
*************************************************************************************************************************************************
struct v4l2_control {
    __u32             id;
    __s32             value;
};
说明:该结构体时用来设置或获取某个id的当前值,通过VIDIOC_G_CTRL,VIDIOC_S_CTRL命令符;

*************************************************************************************************************************************************
struct v4l2_requestbuffers {
    __u32            count;
    __u32            type;        /* enum v4l2_buf_type */
    __u32            memory;        /* enum v4l2_memory */
    __u32            reserved[2];
};
说明:该结构体是用来申请缓存区的,使用VIDIOC_REQBUFS命令符来请求buffer。count参数表示要申请的buffer数量,只有当memory被设置为V4L2_MEMORY_MMAP才可以使用;type就不多说了,还是V4L2_BUF_TYPE_VIDEO_CAPTURE类型;memory参数是应用程序设置的,这里只能设置为V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR。
*************************************************************************************************************************************************
struct v4l2_buffer {
    __u32            index;
    __u32            type;
    __u32            bytesused;
    __u32            flags;
    __u32            field;
    struct timeval        timestamp;
    struct v4l2_timecode    timecode;
    __u32            sequence;

    /* memory location */
    __u32            memory;
    union {
        __u32           offset;
        unsigned long   userptr;
        struct v4l2_plane *planes;
        __s32        fd;
    } m;
    __u32            length;
    __u32            reserved2;
    __u32            reserved;
};
说明:与内存映射相关的结构体,使用VIDIOC_DQBUF或VIDIOC_QBUF命令符来出队或入队一个缓存帧。
*************************************************************************************************************************************************
struct v4l2_streamparm {
    __u32     type;            /* enum v4l2_buf_type */
    union {
        struct v4l2_captureparm    capture;
        struct v4l2_outputparm    output;
        __u8    raw_data[200];  /* user-defined */
    } parm;
};
struct v4l2_captureparm {
    __u32           capability;      /*  Supported modes */
    __u32           capturemode;      /*  Current mode */
    struct v4l2_fract  timeperframe;  /*  Time per frame in seconds */
    __u32           extendedmode;  /*  Driver-specific extensions */
    __u32              readbuffers;   /*  # of buffers for read */
    __u32           reserved[4];
};
struct v4l2_fract {
    __u32   numerator;
    __u32   denominator;
};
说明:视频流参数的设置,使用VIDIOC_G_PARM或VIDIOC_S_PARM命令符来获取或设置视频流参数,比如获取或设置fps,type为V4L2_BUF_TYPE_VIDEO_CAPTURE,parm使用capture成员,而capture结构体中的timeperframe成员保存着帧率信息,两帧相间周期为numerator/denominator,即每秒有denominator帧(当numerator=1时)。当然实际设置和获取的帧率往往和视频显示的不一致,因为中间数据转换的过程有些耗时以及硬件可能达不到实际的设置值。
*************************************************************************************************************************************************

2.我笔记本电脑摄像头可控制的项有:

-----------------------------------------------------------
index:9963776
type:1
name:Brightness
minimum:0
maximum:255
step:1
default_value:128
flags:0
-----------------------------------------------------------
index:9963777
type:1
name:Contrast
minimum:0
maximum:255
step:1
default_value:32
flags:0
-----------------------------------------------------------
index:9963778
type:1
name:Saturation
minimum:0
maximum:100
step:1
default_value:64
flags:0
-----------------------------------------------------------
index:9963779
type:1
name:Hue
minimum:-180
maximum:180
step:1
default_value:0
flags:0
-----------------------------------------------------------
index:9963788
type:2
name:White Balance Temperature, Auto
minimum:0
maximum:1
step:1
default_value:1
flags:0
-----------------------------------------------------------
index:9963792
type:1
name:Gamma
minimum:90
maximum:150
step:1
default_value:120
flags:0
-----------------------------------------------------------
index:9963800
type:3
name:Power Line Frequency
minimum:0
maximum:2
step:1
default_value:1
flags:0
-----------------------------------------------------------
index:9963802
type:1
name:White Balance Temperature
minimum:2500
maximum:6500
step:10
default_value:4500
flags:16
-----------------------------------------------------------
index:9963803
type:1
name:Sharpness
minimum:0
maximum:7
step:1
default_value:0
flags:0
-----------------------------------------------------------
index:9963804
type:1
name:Backlight Compensation
minimum:0
maximum:2
step:1
default_value:0
flags:0
-----------------------------------------------------------
index:0
type:3
name:Exposure, Auto
minimum:0
maximum:3
step:1
default_value:3
flags:0
-----------------------------------------------------------
index:0
type:1
name:Exposure (Absolute)
minimum:2
maximum:1250
step:1
default_value:156
flags:16
-----------------------------------------------------------
index:0
type:2
name:Exposure, Auto Priority
minimum:0
maximum:1
step:1
default_value:0
flags:0
-----------------------------------------------------------
一般的根据index来获取某一项的具体属性,然后在Qt界面可以使用滚动条来控制具体的数值,最大最小以及步长都有了,还有恢复默认值的default_value值。

3.在学习期间做了个Demo,也打了包,使用V4l2去获取硬件设备的信息,以及控制视频流的打开关闭,各种用户参数和扩展参数的获取和设置,最终将读取的帧画面进行格式转换为Qt支持的格式显示在QLabel上,以下是一些截图,最后在放上打包的程序,至于源码和自己二次封装的库等以后修改差不多了在上传吧。打包后的Demo下载(如何不要下载积分?)

V4L2+Qt5实现摄像头视频采集以及参数控制

V4L2+Qt5实现摄像头视频采集以及参数控制

V4L2+Qt5实现摄像头视频采集以及参数控制