V4L2 摄像头应用编程

news2024/11/16 7:52:56

目录

  • V4L2 简介
  • V4L2 摄像头应用程序
    • 打开摄像头
    • 查询设备的属性/能力/功能
    • 设置帧格式、帧率

ALPHA/Mini I.MX6U 开发板配套支持多种不同的摄像头,包括正点原子的ov5640(500W 像素)、
ov2640(200W 像素)以及ov7725(不带FIFO、30W 像素)这三款摄像头,在开发板出厂系统上,可以使用这些摄像头;当然,除此之外我们还可以使用USB 摄像头,直接将USB 摄像头插入到开发板上的USB
接口即可!本章我们就来学习Linux 下的摄像头应用编程。

V4L2 简介

大家可以看到我们本章的标题叫做“V4L2 摄像头应用编程”,那什么是V4L2 呢?对Linux 下摄像头驱动程序开发有过了解的读者,应该知道这是什么意思。
V4L2 是Video for linux two 的简称,是Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范,那什么是视频类设备呢?一个非常典型的视频类设备就是视频采集设备,譬如各种摄像头;当然还包括其它类型视频类设备,这里就不再给介绍了。
使用V4L2 设备驱动框架注册的设备会在Linux 系统/dev/目录下生成对应的设备节点文件,设备节点的名称通常为videoX(X 标准一个数字编号,0、1、2、3……),每一个videoX 设备文件就代表一个视频类设备。应用程序通过对videoX 设备文件进行I/O 操作来配置、使用设备类设备,下小节将向大家详细介绍!
图25.1.1 video 类设备节点

V4L2 摄像头应用程序

V4L2 设备驱动框架向应用层提供了一套统一、标准的接口规范,应用程序按照该接口规范来进行应用编程,从而使用摄像头。对于摄像头设备来说,其编程模式如下所示:

  1. 首先是打开摄像头设备;
  2. 查询设备的属性或功能;
  3. 设置设备的参数,譬如像素格式、帧大小、帧率;
  4. 申请帧缓冲、内存映射;
  5. 帧缓冲入队;
  6. 开启视频采集;
  7. 帧缓冲出队、对采集的数据进行处理;
  8. 处理完后,再次将帧缓冲入队,往复;
  9. 结束采集。
    流程图如下所示:
    在这里插入图片描述
    从流程图中可以看到,几乎对摄像头的所有操作都是通过ioctl()来完成,搭配不同的V4L2 指令(request
    参数)请求不同的操作,这些指令定义在头文件linux/videodev2.h 中,在摄像头应用程序代码中,需要包含头文件linux/videodev2.h,该头文件中申明了很多与摄像头应用编程相关的数据结构以及宏定义,大家可以打开这个头文件看看。
    在videodev2.h 头文件中,定义了很多ioctl()的指令,以宏定义的形式提供(VIDIOC_XXX),如下所示:
/*
 * I O C T L C O D E S F O R V I D E O D E V I C E S
 *
 */
#define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability)
#define VIDIOC_RESERVED _IO('V', 1)
#define VIDIOC_ENUM_FMT _IOWR('V', 2, struct v4l2_fmtdesc)
#define VIDIOC_G_FMT _IOWR('V', 4, struct v4l2_format)
#define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
#define VIDIOC_REQBUFS _IOWR('V', 8, struct v4l2_requestbuffers)
#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v4l2_buffer)
#define VIDIOC_G_FBUF _IOR('V', 10, struct v4l2_framebuffer)
#define VIDIOC_S_FBUF _IOW('V', 11, struct v4l2_framebuffer)
#define VIDIOC_OVERLAY _IOW('V', 14, int)
#define VIDIOC_QBUF _IOWR('V', 15, struct v4l2_buffer)
#define VIDIOC_EXPBUF _IOWR('V', 16, struct v4l2_exportbuffer)
#define VIDIOC_DQBUF _IOWR('V', 17, struct v4l2_buffer)
#define VIDIOC_STREAMON _IOW('V', 18, int)
#define VIDIOC_STREAMOFF _IOW('V', 19, int)
#define VIDIOC_G_PARM _IOWR('V', 21, struct v4l2_streamparm)
#define VIDIOC_S_PARM _IOWR('V', 22, struct v4l2_streamparm)
#define VIDIOC_G_STD _IOR('V', 23, v4l2_std_id)
#define VIDIOC_S_STD _IOW('V', 24, v4l2_std_id)
#define VIDIOC_ENUMSTD _IOWR('V', 25, struct v4l2_standard)
#define VIDIOC_ENUMINPUT _IOWR('V', 26, struct v4l2_input)
#define VIDIOC_G_CTRL _IOWR('V', 27, struct v4l2_control)
#define VIDIOC_S_CTRL _IOWR('V', 28, struct v4l2_control)
#define VIDIOC_G_TUNER _IOWR('V', 29, struct v4l2_tuner)
#define VIDIOC_S_TUNER _IOW('V', 30, struct v4l2_tuner)
#define VIDIOC_G_AUDIO _IOR('V', 33, struct v4l2_audio)
#define VIDIOC_S_AUDIO _IOW('V', 34, struct v4l2_audio)
#define VIDIOC_QUERYCTRL _IOWR('V', 36, struct v4l2_queryctrl)
#define VIDIOC_QUERYMENU _IOWR('V', 37, struct v4l2_querymenu)
#define VIDIOC_G_INPUT _IOR('V', 38, int)
#define VIDIOC_S_INPUT _IOWR('V', 39, int)
#define VIDIOC_G_EDID _IOWR('V', 40, struct v4l2_edid)
#define VIDIOC_S_EDID _IOWR('V', 41, struct v4l2_edid)
#define VIDIOC_G_OUTPUT _IOR('V', 46, int)
#define VIDIOC_S_OUTPUT _IOWR('V', 47, int)
#define VIDIOC_ENUMOUTPUT _IOWR('V', 48, struct v4l2_output)
#define VIDIOC_G_AUDOUT _IOR('V', 49, struct v4l2_audioout)
#define VIDIOC_S_AUDOUT _IOW('V', 50, struct v4l2_audioout)
#define VIDIOC_G_MODULATOR _IOWR('V', 54, struct v4l2_modulator)
#define VIDIOC_S_MODULATOR _IOW('V', 55, struct v4l2_modulator)
#define VIDIOC_G_FREQUENCY _IOWR('V', 56, struct v4l2_frequency)
#define VIDIOC_S_FREQUENCY _IOW('V', 57, struct v4l2_frequency)
#define VIDIOC_CROPCAP _IOWR('V', 58, struct v4l2_cropcap)
#define VIDIOC_G_CROP _IOWR('V', 59, struct v4l2_crop)
#define VIDIOC_S_CROP _IOW('V', 60, struct v4l2_crop)
#define VIDIOC_G_JPEGCOMP _IOR('V', 61, struct v4l2_jpegcompression)
#define VIDIOC_S_JPEGCOMP _IOW('V', 62, struct v4l2_jpegcompression)
#define VIDIOC_QUERYSTD _IOR('V', 63, v4l2_std_id)
#define VIDIOC_TRY_FMT _IOWR('V', 64, struct v4l2_format)
#define VIDIOC_ENUMAUDIO _IOWR('V', 65, struct v4l2_audio)
#define VIDIOC_ENUMAUDOUT _IOWR('V', 66, struct v4l2_audioout)
#define VIDIOC_G_PRIORITY _IOR('V', 67, __u32) /* enum v4l2_priority */
#define VIDIOC_S_PRIORITY _IOW('V', 68, __u32) /* enum v4l2_priority */
#define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap)
#define VIDIOC_LOG_STATUS _IO('V', 70)
#define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls)
#define VIDIOC_S_EXT_CTRLS _IOWR('V', 72, struct v4l2_ext_controls)
#define VIDIOC_TRY_EXT_CTRLS _IOWR('V', 73, struct v4l2_ext_controls)
#define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum)
#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum)
#define VIDIOC_G_ENC_INDEX _IOR('V', 76, struct v4l2_enc_idx)
#define VIDIOC_ENCODER_CMD _IOWR('V', 77, struct v4l2_encoder_cmd)
#define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd)

每一个不同的指令宏就表示向设备请求不同的操作,从上面可以看到,每一个宏后面(_IOWR/_IOR/_IOW)还携带了一个struct 数据结构体,譬如struct v4l2_capability、struct v4l2_fmtdesc,这就是调用ioctl()时需要传入的第三个参数的类型;调用ioctl()前,定义一个该类型变量,调用ioctl()时、将变量的指针作为ioctl()的第三个参数传入,譬如:

struct v4l2_capability cap;
……
ioctl(fd, VIDIOC_QUERYCAP, &cap);

在实际的应用编程中,并不是所有的指令都会用到,针对视频采集类设备,以下笔者列出了一些常用的指令:
在这里插入图片描述

打开摄像头

视频类设备对应的设备节点为/dev/videoX,X 为数字编号,通常从0 开始;摄像头应用编程的第一步便是打开设备,调用open 打开,得到文件描述符fd,如下所示:

int fd = -1;
/* 打开摄像头*/
fd = open("/dev/video0", O_RDWR);
if (0 > fd)
{
    fprintf(stderr, "open error: %s: %s\n", "/dev/video0", strerror(errno));
    return -1;
}

打开设备文件时,需要使用O_RDWR 指定读权限和写权限。

查询设备的属性/能力/功能

打开设备之后,接着需要查询设备的属性,确定该设备是否是一个视频采集类设备、以及其它一些属性,怎么查询呢?自然是通过ioctl()函数来实现,ioctl()对于设备文件来说是一个非常重要的系统调用,凡是涉及到配置设备、获取设备配置等操作都会使用ioctl 来完成,在前面章节内容中我们就已经见识过了;但对于普通文件来说,ioctl()几乎没什么用。
查询设备的属性,使用的指令为VIDIOC_QUERYCAP,如下所示:
ioctl(int fd, VIDIOC_QUERYCAP, struct v4l2_capability *cap);
此时通过ioctl()将获取到一个struct v4l2_capability 类型数据,struct v4l2_capability 数据结构描述了设备的一些属性,结构体定义如下所示:

struct v4l2_capability
{
    __u8 driver[16];    /* 驱动的名字*/
    __u8 card[32];      /* 设备的名字*/
    __u8 bus_info[32];  /* 总线的名字*/
    __u32 version;      /* 版本信息*/
    __u32 capabilities; /* 设备拥有的能力*/
    __u32 device_caps;
    __u32 reserved[3]; /* 保留字段*/
};

我们重点关注的是capabilities 字段,该字段描述了设备拥有的能力,该字段的值如下(可以是以下任意一个值或多个值的位或关系):

/* 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_META_CAPTURE 0x00800000   /* Is a metadata capture 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_TOUCH 0x10000000          /* Is a touch device */
#define V4L2_CAP_DEVICE_CAPS 0x80000000    /* sets device capabilities field */

这些宏都是在videodev2.h 头文件中所定义的,大家可以自己去看。对于摄像头设备来说,它的capabilities
字段必须包含V4L2_CAP_VIDEO_CAPTURE,表示它支持视频采集功能。所以我们可以通过判断capabilities
字段是否包含V4L2_CAP_VIDEO_CAPTURE、来确定它是否是一个摄像头设备,譬如:

/* 查询设备功能*/
ioctl(fd, VIDIOC_QUERYCAP, &vcap);
/* 判断是否是视频采集设备*/
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities))
{
    fprintf(stderr, "Error: No capture video device!\n");
    return -1;
}

设置帧格式、帧率

一个摄像头通常会支持多种不同的像素格式,譬如RGB、YUYV 以及压缩格式MJPEG 等,并且还支持多种不同的视频采集分辨率,譬如640480、320240、1280*720 等,除此之外,同一分辨率可能还支持多种不同的视频采集帧率(15fps、30fps)。所以,通常在进行视频采集之前、需要在应用程序中去设置这些参数。
a)枚举出摄像头支持的所有像素格式:VIDIOC_ENUM_FMT
要设置像素格式,首先得知道该设备支持哪些像素格式,如何得知呢?使用VIDIOC_ENUM_FMT 指令:
ioctl(int fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *fmtdesc);
使用VIDIOC_ENUM_FMT 可以枚举出设备所支持的所有像素格式,调用ioctl()需要传入一个struct v4l2_fmtdesc *指针,ioctl()会将获取到的数据写入到fmtdesc 指针所指向的对象中。struct v4l2_fmtdesc 结构体描述了像素格式相关的信息,我们来看看struct v4l2_fmtdesc 结构体的定义:

/*
 * F O R M A T E N U M E R A T I O N
 */
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];
};

index 表示编号,在枚举之前,需将其设置为0,然后每次ioctl()调用之后将其值加1。一次ioctl()调用只能得到一种像素格式的信息,如果设备支持多种像素格式,则需要循环调用多次,通过index 来控制,
index 从0 开始,调用一次ioctl()之后加1,直到ioctl()调用失败,表示已经将所有像素格式都枚举出来了;所以index 就是一个编号,获取index 编号指定的像素格式。
description 字段是一个简单地描述性字符串,简单描述pixelformat 像素格式。
pixelformat 字段则是对应的像素格式编号,这是一个无符号32 位数据,每一种像素格式都会使用一个
u32 类型数据来表示,如下所示:

/* RGB formats */
#define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R', 'G', 'B', '1')  /* 8 RGB-3-3-2 */
#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R', '4', '4', '4')  /* 16 xxxxrrrr ggggbbbb */
#define V4L2_PIX_FMT_ARGB444 v4l2_fourcc('A', 'R', '1', '2') /* 16 aaaarrrr ggggbbbb */
#define V4L2_PIX_FMT_XRGB444 v4l2_fourcc('X', 'R', '1', '2') /* 16 xxxxrrrr ggggbbbb */
#define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R', 'G', 'B', 'O')  /* 16 RGB-5-5-5 */
#define V4L2_PIX_FMT_ARGB555 v4l2_fourcc('A', 'R', '1', '5') /* 16 ARGB-1-5-5-5 */
#define V4L2_PIX_FMT_XRGB555 v4l2_fourcc('X', 'R', '1', '5') /* 16 XRGB-1-5-5-5 */
#define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P')  /* 16 RGB-5-6-5 */
......
/* Grey formats */
#define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */
#define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ')   /* 4 Greyscale */
#define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ')   /* 6 Greyscale */
#define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ')  /* 10 Greyscale */
    ......
/* Luminance+Chrominance formats */
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y', 'Y', 'U', 'V') /* 16 YUV 4:2:2 */
#define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
#define V4L2_PIX_FMT_UYVY v4l2_fourcc('U', 'Y', 'V', 'Y') /* 16 YUV 4:2:2 */
    ......
/* compressed formats */
#define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
#define V4L2_PIX_FMT_JPEG v4l2_fourcc('J', 'P', 'E', 'G')  /* JFIF JPEG */
#define V4L2_PIX_FMT_DV v4l2_fourcc('d', 'v', 's', 'd')    /* 1394 */
#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G')  /* MPEG-1/2/4 Multiplexed */

以上列举出来的只是其中一部分,篇幅有限、不能将所有的像素格式都列举出来,大家可以自己查看
videodev2.h 头文件。可以看到后面有一个v4l2_fourcc 宏,其实就是通过这个宏以及对应的参数合成的一个
u32 类型数据。
type 字段指定类型,表示我们要获取设备的哪种功能对应的像素格式,因为有些设备它可能即支持视频采集功能、又支持视频输出等其它的功能;type 字段可取值如下:

enum v4l2_buf_type
{
    V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, // 视频采集
    V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,  // 视频输出
    V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
    V4L2_BUF_TYPE_VBI_CAPTURE = 4,
    V4L2_BUF_TYPE_VBI_OUTPUT = 5,
    V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
    V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,
    V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
    V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
    V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
    V4L2_BUF_TYPE_SDR_CAPTURE = 11,
    V4L2_BUF_TYPE_SDR_OUTPUT = 12,
    V4L2_BUF_TYPE_META_CAPTURE = 13,
    /* Deprecated, do not use */
    V4L2_BUF_TYPE_PRIVATE = 0x80,
};

type 字段需要在调用ioctl() 之前设置它的值,对于摄像头,需要将type 字段设置为
V4L2_BUF_TYPE_VIDEO_CAPTURE,指定我们将要获取的是视频采集的像素格式。
使用示例如下所示:

struct v4l2_fmtdesc fmtdesc;
/* 枚举出摄像头所支持的所有像素格式以及描述信息*/
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (0 == ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
{
    printf("fmt: %s <0x%x>\n", fmtdesc.description, fmtdesc.pixelformat);
    fmtdesc.index++;
}

b)枚举摄像头所支持的所有视频采集分辨率:VIDIOC_ENUM_FRAMESIZES
使用VIDIOC_ENUM_FRAMESIZES 指令可以枚举出设备所支持的所有视频采集分辨率,用法如下所示:

ioctl(int fd, VIDIOC_ENUM_FRAMESIZES, struct v4l2_frmsizeenum *frmsize);

调用ioctl()需要传入一个struct v4l2_frmsizeenum *指针,ioctl()会将获取到的数据写入到frmsize 指针所指向的对象中。struct v4l2_frmsizeenum 结构体描述了视频帧大小相关的信息,我们来看看struct v4l2_frmsizeenum 结构体的定义:

struct v4l2_frmsizeenum
{
    __u32 index;        /* Frame size number */
    __u32 pixel_format; /* 像素格式*/
    __u32 type;         /* type */
    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] */
};

index 字段与struct v4l2_fmtdesc 结构体的index 字段意义相同,一个摄像头通常支持多种不同的视频采集分辨率,一次ioctl()调用只能得到一种视频帧大小信息,如果设备支持多种视频帧大小,则需要循环调用多次,通过index 来控制。
pixel_format 字段指定像素格式,而type 字段与struct v4l2_fmtdesc 结构体的type 字段意义相同;在调用ioctl()之前,需要先设置type 字段与pixel_format 字段,确定我们将要枚举的是:设备的哪种功能、哪种像素格式支持的视频帧大小。
可以看到struct v4l2_frmsizeenum 结构体中有一个union 共用体,type= V4L2_BUF_TYPE_VIDEO_CAPTURE 情况下,discrete 生效,这是一个struct v4l2_frmsize_discrete 类型变量,描述了视频帧大小信息(包括视频帧的宽度和高度),也就是视频采集分辨率大小。
譬如我们要枚举出摄像头RGB565 像素格式所支持的所有视频帧大小:

struct v4l2_frmsizeenum frmsize;
frmsize.index = 0;
frmsize.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmsize.pixel_format = V4L2_PIX_FMT_RGB565;
while (0 == ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize))
{
    printf("frame_size<%d*%d>\n", frmsize.discrete.width, frmsize.discrete.height);
    frmsize.index++;
}

c)枚举摄像头所支持的所有视频采集帧率:VIDIOC_ENUM_FRAMEINTERVALS
同一种视频帧大小,摄像头可能会支持多种不同的视频采集帧率,譬如常见的15fps、30fps、45fps 以及60fps 等;使用VIDIOC_ENUM_FRAMEINTERVALS 指令可以枚举出设备所支持的所有帧率,使用方式如下:

ioctl(int fd, VIDIOC_ENUM_FRAMEINTERVALS, struct v4l2_frmivalenum *frmival);

调用ioctl()需要传入一个struct v4l2_frmivalenum *指针,ioctl()会将获取到的数据写入到frmival 指针所指向的对象中。struct v4l2_frmivalenum 结构体描述了视频帧率相关的信息,我们来看看struct v4l2_frmivalenum 结构体的定义:

struct v4l2_frmivalenum
{
    __u32 index;        /* Frame format index */
    __u32 pixel_format; /* Pixel format */
    __u32 width;        /* Frame width */
    __u32 height;       /* Frame height */
    __u32 type;         /* type */
    union
    { /* Frame interval */
        struct v4l2_fract discrete;
        struct v4l2_frmival_stepwise stepwise;
    };
    __u32 reserved[2]; /* Reserved space for future use */
};
struct v4l2_fract
{
    __u32 numerator;   // 分子
    __u32 denominator; // 分母
};

index、type 字段与struct v4l2_frmsizeenum 结构体的index、type 字段意义相同。
width、height 字段用于指定视频帧大小,pixel_format 字段指定像素格式。
以上这些字段都是需要在调用ioctl()之前设置它的值。
可以看到struct v4l2_frmivalenum 结构体也有一个union 共用体,当type= V4L2_BUF_TYPE_VIDEO_CAPTURE 时,discrete 生效,这是一个struct v4l2_fract 类型变量,描述了视频帧率信息(一秒钟采集图像的次数);struct v4l2_fract 结构体中,numerator 表示分子、denominator 表示分母,使用numerator / denominator 来表示图像采集的周期(采集一幅图像需要多少秒),所以视频帧率便等于denominator / numerator。
使用示例,譬如,我们要枚举出RGB565 像素格式下640*480 帧大小所支持的所有视频采集帧率:

struct v4l2_frmivalenum frmival;
frmival.index = 0;
frmival.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
frmival.pixel_format = V4L2_PIX_FMT_RGB565;
frmival.width = 640;
frmival.height = 480;
while (0 == ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival))
{
    printf("Frame interval<%ffps> ", frmival.discrete.denominator / frmival.discrete.numerator);
    frmival.index++;
}

d)查看或设置当前的格式:VIDIOC_G_FMT、VIDIOC_S_FMT
前面介绍的指令只是枚举设备支持的像素格式、视频帧大小以及视频采集帧率等这些信息,将下来我们将介绍如何设置这些参数。
首先可以使用VIDIOC_G_FMT 指令查看设备当期的格式,用法如下所示

int ioctl(int fd, VIDIOC_G_FMT, struct v4l2_format *fmt);

调用ioctl()需要传入一个struct v4l2_format *指针,ioctl()会将获取到的数据写入到fmt 指针所指向的对象中,struct v4l2_format 结构体描述了格式相关的信息。
使用VIDIOC_S_FMT 指令设置设备的格式,用法如下所示:

int ioctl(int fd, VIDIOC_S_FMT, struct v4l2_format *fmt);

ioctl()会使用fmt 所指对象的数据去设置设备的格式。我们来看看v4l2_format 结构体的定义:

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 */
        struct v4l2_meta_format meta;         /* V4L2_BUF_TYPE_META_CAPTURE */
        __u8 raw_data[200];                   /* user-defined */
    } fmt;
};

type 字段依然与前面介绍的结构体中的type 字段意义相同,不管是获取格式、还是设置格式都需要在调用ioctl()函数之前设置它的值。
接下来是一个union 共用体,当type 被设置为V4L2_BUF_TYPE_VIDEO_CAPTURE 时,pix 变量生效,它是一个struct v4l2_pix_format 类型变量,记录了视频帧格式相关的信息,如下所示:

struct v4l2_pix_format
{
    __u32 width;        // 视频帧的宽度(单位:像素)
    __u32 height;       // 视频帧的高度(单位:像素)
    __u32 pixelformat;  // 像素格式
    __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_*) */
    union
    {
        /* enum v4l2_ycbcr_encoding */
        __u32 ycbcr_enc;
        /* enum v4l2_hsv_encoding */
        __u32 hsv_enc;
    };
    __u32 quantization; /* enum v4l2_quantization */
    __u32 xfer_func;    /* enum v4l2_xfer_func */
};

colorspace 字段描述的是一个颜色空间,可取值如下:

enum v4l2_colorspace
{
    /*
     * Default colorspace, i.e. let the driver figure it out.
     * Can only be used with video capture.
     */
    V4L2_COLORSPACE_DEFAULT = 0,
    /* SMPTE 170M: used for broadcast NTSC/PAL SDTV */
    V4L2_COLORSPACE_SMPTE170M = 1,
    /* Obsolete pre-1998 SMPTE 240M HDTV standard, superseded by Rec 709 */
    V4L2_COLORSPACE_SMPTE240M = 2,
    /* Rec.709: used for HDTV */
    V4L2_COLORSPACE_REC709 = 3,
    /*
     * Deprecated, do not use. No driver will ever return this. This was
     * based on a misunderstanding of the bt878 datasheet.
     */
    V4L2_COLORSPACE_BT878 = 4,
    /*
     * NTSC 1953 colorspace. This only makes sense when dealing with
     * really, really old NTSC recordings. Superseded by SMPTE 170M.
     */
    V4L2_COLORSPACE_470_SYSTEM_M = 5,
    /*
     * EBU Tech 3213 PAL/SECAM colorspace. This only makes sense when
     * dealing with really old PAL/SECAM recordings. Superseded by
     * SMPTE 170M.
     */
    V4L2_COLORSPACE_470_SYSTEM_BG = 6,
    /*
     * Effectively shorthand for V4L2_COLORSPACE_SRGB, V4L2_YCBCR_ENC_601
     * and V4L2_QUANTIZATION_FULL_RANGE. To be used for (Motion-)JPEG.
     */
    V4L2_COLORSPACE_JPEG = 7,
    /* For RGB colorspaces such as produces by most webcams. */
    V4L2_COLORSPACE_SRGB = 8,
    /* AdobeRGB colorspace */
    V4L2_COLORSPACE_ADOBERGB = 9,
    /* BT.2020 colorspace, used for UHDTV. */
    V4L2_COLORSPACE_BT2020 = 10,
    /* Raw colorspace: for RAW unprocessed images */
    V4L2_COLORSPACE_RAW = 11,
    /* DCI-P3 colorspace, used by cinema projectors */
    V4L2_COLORSPACE_DCI_P3 = 12,
};

使用VIDIOC_S_FMT 指令设置格式时,通常不需要用户指定colorspace,底层驱动会根据像素格式
pixelformat 来确定对应的colorspace。
例子:获取当前的格式、并设置格式

struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 > ioctl(fd, VIDIOC_G_FMT, &fmt))
{ // 获取格式信息
    perror("ioctl error");
    return -1;
}
printf("width:%d, height:%d format:%d\n", fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.pixelformat);
fmt.fmt.pix.width = 800;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
if (0 > ioctl(fd, VIDIOC_S_FMT, &fmt))
{ // 设置格式
    perror("ioctl error");
    return -1;
}

使用指令VIDIOC_S_FMT 设置格式时,实际设置的参数并不一定等于我们指定的参数,譬如上面我们指定视频帧宽度为800、高度为480,但这个摄像头不一定支持这种视频帧大小,或者摄像头不支持
V4L2_PIX_FMT_RGB565 这种像素格式;通常在这种情况下,底层驱动程序并不会按照我们指定的参数进行设置,它会对这些参数进行修改,譬如,如果摄像头不支持800480,那么底层驱动可能会将其修改为
640
480(假设摄像头支持这种分辨率);所以,当ioctl()调用返回后,我们还需要检查返回的struct v4l2_format
类型变量,以确定我们指定的参数是否已经生效:

struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 800;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
if (0 > ioctl(fd, VIDIOC_S_FMT, &fmt))
{ // 设置格式
    perror("ioctl error");
    return -1;
}
if (800 != fmt.fmt.pix.width ||
    480 != fmt.fmt.pix.height)
{
    do_something();
}
if (V4L2_PIX_FMT_RGB565 != fmt.fmt.pix.pixelformat)
{
    do_something();
}

e)设置或获取当前的流类型相关参数:VIDIOC_G_PARM、VIDIOC_S_PARM
使用VIDIOC_G_PARM 指令可以获取设备的流类型相关参数(Stream type-dependent parameters),使用方式如下:

ioctl(int fd, VIDIOC_G_PARM, struct v4l2_streamparm *streamparm);

调用ioctl()需要传入一个struct v4l2_streamparm *指针,ioctl()会将获取到的数据写入到streamparm 指针所指向的对象中,struct v4l2_streamparm 结构体描述了流类型相关的信息,具体的内容等会在介绍。
使用VIDIOC_S_PARM 指令设置设备的流类型相关参数,用法如下所示:

ioctl(int fd, VIDIOC_S_PARM, struct v4l2_streamparm *streamparm);

ioctl() 会使用streamparm 所指对象的数据去设置设备的流类型相关参数。我们来看看struct v4l2_streamparm 结构体的定义:

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; /* 分母*/
};

type 字段与前面一样,不再介绍,在调用ioctl()之前需先设置它的值。
当type= V4L2_BUF_TYPE_VIDEO_CAPTURE 时,union 共用体中capture 变量生效,它是一个struct v4l2_captureparm 类型变量,struct v4l2_captureparm 结构体描述了摄像头采集相关的一些参数,譬如视频采集帧率,上面已经给出了该结构体的定义。
struct v4l2_captureparm 结构体中,capability 字段表示设备支持的模式有哪些,可取值如下(以下任意一个或多个的位或关系):

/* Flags for 'capability' and 'capturemode' fields */
#define V4L2_MODE_HIGHQUALITY 0x0001 /* High quality imaging mode 高品质成像模式*/
#define V4L2_CAP_TIMEPERFRAME 0x1000 /* timeperframe field is supported 支持设置timeperframe
字段*/

capturemode 则表示当前的模式,与capability 字段的取值相同。
timeperframe 字段是一个struct v4l2_fract 结构体类型变量,描述了设备视频采集的周期,前面已经给大家介绍过。使用VIDIOC_S_PARM 可以设置视频采集的周期,也就是视频采集帧率,但是很多设备并不支持应用层设置timeperframe 字段,只有当capability 字段包含V4L2_CAP_TIMEPERFRAME 时才表示设备支持timeperframe 字段,这样应用层才可以去设置设备的视频采集帧率。
所以,在设置之前,先通过VIDIOC_G_PARM 命令获取到设备的流类型相关参数,判断capability 字段是否包含V4L2_CAP_TIMEPERFRAME,如下所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/947641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java定时任务cron表达式每周执行一次,执行时间与实际时间不一样的坑!!!

java springboot 利用schedule执行定时任务是很常用的功能&#xff0c;有一个很常用的网站就是在线Cron表达式生成器&#xff0c;但是在这个网站最近遇到一个坑。 我要每周一下午一点执行一次&#xff0c;我把我写的表达式在这个网站验证了&#xff0c;是可以的&#xff0c;况…

VSCode - 一键删除每行前面的行号数字

ctrl f 打开查找 输入正则表达式&#xff0c;并点击使用正则查找&#xff1a; 带点的&#xff1a;^\s*([0-9])\. 不带点&#xff1a;^\s*([0-9]) 综合起来&#xff1a;^\s*([0-9])[\.]* 替换为空格

手机怎么剪视频?分享一些剪辑工具和注意事项

视频剪辑是一种将多个视频片段进行剪切、合并和编辑的技术&#xff0c;它可以帮助我们制作出精彩的视频作品。如今&#xff0c;随着智能手机的普及&#xff0c;我们可以随时随地使用手机进行视频剪辑。本文将为大家介绍一些手机剪辑工具和注意事项&#xff0c;帮助大家更好地进…

全新抖音快手小红书去水印系统网站源码 | 支持几十种平台

全新抖音快手小红书去水印系统网站源码 | 支持几十种平台

Zynq GTX全网最细讲解,aurora 8b/10b编解码,OV5640摄像头视频传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTX 全网最细解读GTX 基本结构GTX 发送和接收处理流程GTX 的参考时钟GTX 发送接口GTX 接收接口GTX IP核调用和使用 4、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条视频数据组包GTX aurora 8b/10b数据对…

INSTANCE 2022数据集

论文链接&#xff1a;https://arxiv.org/abs/2301.03281 数据集链接&#xff1a;Home - Grand Challenge github baseline&#xff1a;GitHub - PerceptionComputingLab/INSTANCE2022: Official repository of MICCAI 2022 INSTANCE challenge 数据集得自己填表申请 比赛是…

gdb基本使用快捷键

gdb 1. gdb简介2.基本命令2.1 进入/退出2.2 基本使用2.3 执行语句2.4 查看变量2.5 禁用/启用断点 1. gdb简介 gdb是Linux下的代码调试工具。 程序的发布有debug模式和release模式&#xff0c;Linux的gcc/g模式&#xff0c;默认是release模式。若想在Linux下调试&#xff0c;程…

生活类书单视频如何做?几个步骤轻松拿捏

生活类书单视频是一种很受欢迎的内容形式&#xff0c;它可以帮助观众了解各种生活类书籍&#xff0c;并提供一些有用的信息。在制作生活类书单视频时&#xff0c;我们需要注意几个步骤&#xff0c;以确保视频内容的质量和专业性。 首先&#xff0c;我们需要选择适合的书单背景。…

华为数通方向HCIP-DataCom H12-821题库(拖拽题,知识点总结)

以下是我在现有题库中整理的需要重点关注的考点内容&#xff0c;如有遗漏小伙伴可以留言补充。 VRRP双机热备份&#xff1a; 主备AC两个独立的IP地址&#xff0c;通过VRRP对外虚拟为同一个IP地址&#xff0c;单个AP和虚拟IP建立一条CAPWAP链路。主AC备份AP信息、STA信息和CAPWA…

关于三维模型OBJ格式轻量化压缩必要性探讨

关于三维模型OBJ格式轻量化压缩必要性探讨 三维模型的OBJ格式轻量化压缩在当前的计算机图形学和虚拟现实应用中具有重要的必要性。以下是对三维模型OBJ格式轻量化压缩必要性的分析&#xff1a; 1、提高加载和传输效率&#xff1a;随着三维模型的复杂性不断增加&#xff0c;原始…

【day10】驱动

作业&#xff1a; 基于platform实现 添加设备树节点 irq_led{ compatible “hqyj,irq_led”; //用于获取节点 interrupt-parent <&gpiof>; //引用父节点 interrupts <9 0>; //这个节点引入的中断管脚 led1<&gpioe 10 0>; }; 1.驱动端 #include…

火狐浏览器使用scss嵌套编写css无法识别问题

火狐浏览器使用scss嵌套编写css无法识别问题 版本&#xff1a; “node-sass”: “^4.14.1”, “sass-loader”: “^7.3.1”,vue版本&#xff1a; v2问题描述&#xff1a; 我的文件目录是这样的&#xff1a; 而在scss文件中我是这样书写的 .vue文件中 在火狐浏览器中 在谷…

基于Android的学生信息管理系统

基于Android的学生信息管理系统 一、系统介绍二、功能展示三、其他系统实现五.获取源码 一、系统介绍 项目名称&#xff1a;学生信息管理系统 主要技术&#xff1a;java、android 开发工具&#xff1a;Eclipse(ADT) 运行工具&#xff1a;只能使用Eclipse 数 据 库&#xf…

用于统计编码时长的Wakapi

本文应网友 suka 的要求折腾&#xff0c;但是否符合他得要求就不得而知了&#xff0c;因为老苏不是程序员&#xff0c;没有这方面的需求&#xff1b; 什么是 Wakatime &#xff1f; WakaTime 是一个可以统计你在不同语言、项目上写代码时长的工具。 它支持多种 IDE、编辑器。 它…

商汤科技半年报出炉:生成式AI商业化成果喜人,驱动四大业务飞跃

撰稿|行星 来源|贝多财经 8月28日&#xff0c;商汤科技&#xff08;HK:0020&#xff09;发布了2023年上半年业绩报告。从财务数据来看&#xff0c;商汤科技在2023年上半年业绩表现稳健&#xff0c;大模型和生成式AI推动各项业务板块取得长足进步。 一、生成式AI表现亮眼&…

Koa + Prisma 快速入门

10分钟掌握 Koa Prisma 实现数据库 CRUD 前言 Prisma 是号称下一代的 ORM 工具&#xff0c;同样是基于 TypeScript 实现&#xff0c;但是带来了很强的类型安全。 本文使用 Koa.js 搭建一个简单的 Web 服务&#xff0c;配合 MySQL 数据库&#xff0c;来演示如何通过 Prisma …

【100天精通python】Day47:python网络编程_Web编程

目录 1 网络编程与web编程 1.1 网络编程 1.2 web编程 2 Web开发概述 3 Web开发基础 3.1 HTTP协议 3.2 Web服务器 3.3 前端基础 3.4 静态服务器 3.5 前后端交互的基本原理 4 WSGI接口 4.1 CGI 简介 4.2 WSGI 简介 4.3 定义 WSGI 接口 4.4 运行 WSGI 服务 4.5…

面霸的自我修养-synchronized

今天是《面霸的自我修养》的第3弹&#xff0c;内容是Java并发编程中至关重要的关键字synchronized&#xff0c;作为面试中的“必考题”&#xff0c;这部分是你必须要充分准备的内容&#xff0c;接下来我们就一起一探究竟吧。 数据来源&#xff1a; 大部分来自于各机构&#x…

SpringWeb(SpringMVC)

目录 SpringWeb介绍 搭建 SpringWeb SpringWeb介绍 Spring Web是一个基于 Servlet API 构建的原始 web 框架&#xff0c;用于构建基于MVC模式的Web应用程序。在 web 层框架历经 Strust1&#xff0c;WebWork&#xff0c;Strust2 等诸多产品的历代更选 之后&#xff0c;目前业界普…

研发规范第九讲:通用类命名规范(重点)

研发规范第九讲&#xff1a;通用类命名规范&#xff08;重点&#xff09; 无规范不成方圆。我自己非常注重搭建项目结构的起步过程&#xff0c;应用命名规范、模块的划分、目录&#xff08;包&#xff09;的命名&#xff0c;我觉得非常重要&#xff0c;如果做的足够好&#xff…