和小白一起学习V4L2采集视频

最近想做一个视频推流拉流的小项目,需要使用V4L2驱动框架,先来学习学习

Video for Linuxtwo(Video4Linux2)简称V4L2,是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频监控系统和嵌入式多媒体终端中都有广泛的应用。
在Linux下,所有外设都被看成一种特殊的文件,成为“设备文件”,可以象访问普通文件一样对其进行读写。一般来说,采用V4L2驱动的摄像头设备文件是/dev/video0。V4L2支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。V4L2在include/linux/videodev2.h文件中定义了一些重要的数据结构,在采集图像的过程中,就是通过对这些数据的操作来获得最终的图像数据。Linux系统V4L2的能力可在Linux内核编译阶段配置,默认情况下都有此开发接口。

一、工作流程

在这里插入图片描述

1、打开设备

int fd = open(/dev/video0”, O_RDWR);

2、取得设备的属性信息,查看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等

struct v4l2_capability VIDIOC_QUERYCAP 

3、选择视频输入,一个视频设备可以有多个视频输入

 struct v4l2_input  VIDIOC_S_INPUT

4、设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式包括宽度和高度等

VIDIOC_S_STD, VIDIOC_S_FMT
struct v4l2_std_id   struct v4l2_format

5、申请帧缓冲区

VIDIOC_REQBUFS
struct v4l2_requestbuffers

6、将申请到的内核空间帧缓冲区映射到用户空间

VIDIOC_QUERYBUF
struct v4l2_buffer

7、将申请到的缓冲区进入视频采集输入队列

VIDIOC_QBUF
struct v4l2_buffer

8、开始视频采集

VIDIOC_STREAMON
enum v4l2_buf_type

9、取出视频输出队列的帧缓冲数据,对数据处理

VIDIOC_DQBUF
struct v4l2_buffer

10、将帧缓冲区重新进入视频采集输入队列,这样便可循环采集帧数据

VIDIOC_QBUF

11、停止视频采集,解除映射

VIDIOC_STREAMOFF

12、关闭视频设备

close(fd)

二、V4L2API与数据结构

1、相关结构体

  struct v4l2_requestbuffers        //申请帧缓冲,对应命令VIDIOC_REQBUFS 
    struct v4l2_capability        //视频设备的功能,对应命令VIDIOC_QUERYCAP 
    struct v4l2_input        //视频输入信息,对应命令VIDIOC_ENUMINPUT
    struct v4l2_standard        //视频的制式,比如PAL,NTSC,对应命令VIDIOC_ENUMSTD 
    struct v4l2_format        //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
    struct v4l2_buffer        //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF 
    struct v4l2_crop        //视频信号矩形边框
    v4l2_std_id        //视频制式

2、相关IOCTL接口命令

VIDIOC_REQBUFS //分配内存 
        VIDIOC_QUERYBUF         //把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址 
        VIDIOC_QUERYCAP        //查询驱动功能 
        VIDIOC_ENUM_FMT        //获取当前驱动支持的视频格式 
        VIDIOC_S_FMT        //设置当前驱动的频捕获格式 
        VIDIOC_G_FMT        //读取当前驱动的频捕获格式 
        VIDIOC_TRY_FMT        //验证当前驱动的显示格式 
        VIDIOC_CROPCAP        //查询驱动的修剪能力 
        VIDIOC_S_CROP        //设置视频信号的矩形边框 
        VIDIOC_G_CROP        //读取视频信号的矩形边框
        VIDIOC_QBUF        //把数据从缓存中读取出来 
        VIDIOC_DQBUF        //把数据放回缓存队列 
        VIDIOC_STREAMON        //开始视频显示函数 
        VIDIOC_STREAMOFF        //结束视频显示函数 
        VIDIOC_QUERYSTD         //检查当前视频设备支持的标准,例如PAL或NTSC。

三、V4L2编程

一、对摄像头的相关配置

1. 设备的打开和关闭:
int fd = open(/dev/video0”, O_RDWR);
close(fd);
2、查询设备属性:VIDIOC_QUERYCAP

相关函数

int ioctl(int fd, int request, struct v4l2_capability *argp);

相关结构体

structv4l2_capability
{
   
__u8 driver[16];     // 驱动名字
__u8 card[32];       // 设备名字
__u8bus_info[32]; // 设备在系统中的位置
__u32 version;       // 驱动版本号
__u32capabilities;  // 设备支持的操作
__u32reserved[4]; // 保留字段
};
capabilities 常用值:
V4L2_CAP_VIDEO_CAPTURE    // 是否支持图像获取
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_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_READWRITE              0x01000000  /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
#define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */

查看设备信息

    ret = ioctl(fd, VIDIOC_QUERYCAP, &argp);
    if(ret == -1){
   
        perror("ioctl");
        exit(EXIT_FAILURE);
    }
    printf("driver_name:%s\ncard :%s\nbus_info:%s\nversion:%u\n",           
               argp.driver, argp.card, argp.bus_info, argp.version);
3、选择视频输入

相关结构体

struct v4l2_input {
                                                                                      
    __u32        index;     /*  Which input */
    __u8         name[32];      /*  Label */
    __u32        type;      /*  Type of input */
    __u32        audioset;      /*  Associated audios (bitfield) */
    __u32        tuner;             /*  enum v4l2_tuner_type */
    v4l2_std_id  std;
    __u32        status;
    __u32        capabilities;
    __u32        reserved[3];
};

index: 应用关注的输入的索引号; 这是惟一一个用户空间设定的字段。驱动要分配索引号给输入,从0开始,依次往上增加。应用想要知道所有可用的输入时,要调用VIDIOC_ENUMINPUT 控制,调用索引号从0开始,并开始递增。 一旦返回EINVAL,应用就知道,输入己经遍历结束了,只要有输入,输入索引号0就一定要存在的。
name[32]: /* 输入的名字,由驱动设定 */
type: 输入的类型 目前有两个值可选:V4L2_INPUT_TYPE_TUNER 和 V4L2_INPUT_TYPE_CAMERA
audioset: 描述那个音频输入可以与此视频输入相关联,音频输入与视频输入一样通过索引号枚举,但并非所有的音频和视频的组合都是可用的,这个字段是一个掩码,代表对于当前枚举出的视频而言,那些音频输入是可以与之关联的,如果没有音频输入可以与之关联,或者只有一个可选,那么就可以简单地把这个字段置为0
tuner: 如果输入是一个调谐器(type字段置为V4L2_INPUT_TYPE_TUNER),这个字段就是会包含一个相应的调谐设备的索引号
std: 描述设备支持哪个或哪些视频标准
status: 给出输入的状态,完整的标识符集可以再V4L2的文档中找到;简而言之,status中设置的每一位都代表一个问题,这些问题包括没有电源,没有信号,没有同频锁 等等
capabilities 设备支持的操作

选择视频输入 相关配置

    struct v4l2_input input;
    memset(&input, 0, sizeof(struct v4l2_input));
    input.index = 0;
    ret = ioctl(fd, VIDIOC_S_INPUT, &input);
    if(ret == -1){
   
        printf("VIDIOC_S_INPUT is error! LINE:%d\n",__LINE__);
        exit(EXIT_FAILURE);
    }   
4、设置与查看帧格式

1、查看硬件所支持的格式
相关结构体

struct v4l2_fmtdesc
{
   	
__u32 index;   // 要查询的格式序号,应用程序设置
enum v4l2_buf_type type;     // 帧类型,应用程序设置
__u32 flags;    // 是否为压缩格式
__u8 description[32];      // 格式名称
__u32 pixelformat; // 格式
__u32 reserved[4]; // 保留
}; 
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,     
    V4L2_BUF_TYPE_META_OUTPUT      = 14,     
    /* Deprecated, do not use */      
    V4L2_BUF_TYPE_PRIVATE              = 0x80,     
};

VIDIOC_ENUM_FMT 命令枚举出所有支持的格式
在存储器中表示图像有很多种方法,市场几乎找不到可以处理所有V4l2所理解的视频格式的设备。驱动不应支持底层硬件不懂的视频格式。实际上在内核中进行格式转换是令人难以接受的。所以驱动必须可以应用选择一个硬件可以支持的格式。第一步就是简单的允许应用查询硬件所支持的格式。VIDIC_EMUM_FMT 就是为此目的而提供的。在驱动内部这个调用会转换为这样一个回调函数
int (vidioc_enum_fmt_cap)(struct file file, void private_data, struct v4l2_fmtdesc f);
这个回调函数要求视频捕捉设备描述其支持的格。应用会传递一个v4l2_fmdesc结构体
应用会设置index和type字段。index是用来区别格式的一个整形数;与V4l2所使用的其他索引一样,这个也是从0开始递增至最大允许值为止,应用可以通过一直递增索引值index直到返回EINVAL的方式枚举所有支持的格式。
type字段描述的是数据流类型;对于视频捕捉设备来说(摄像头)
就是V4L2_BUF_TYPE_VIDEO_CAPTURE

上述的回掉函数只作用于视频捕获设备
;只有当type 字段的是值是 V4L2_BUF_TYPE_VIDEO_CAPTURE时才会调用。

查看当前摄像头支持的格式

struct v4l2_fmtdesc fmtdesc;
fmtdesc.index = 0;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("fm:\n");
while(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1){
     
    printf("%d.%s   %c%c%c%c\n", fmtdesc.index + 1, fmtdesc.description, 
        fmtdesc.pixelformat &  0xFF,
        (fmtdesc.pixelformat >> 8) & 0xFF,
        (fmtdesc.pixelformat >> 16) & 0xFF,
        (fmtdesc.pixelformat >> 24) & 0xFF);
    fmtdesc.index++;
}

关于pixelformat的值 >>8 >> 16 >> 24 的原因
在头文件#include<linux/videodev2.h>中有定义

#define v4l2_fourcc(a, b, c, d)
((__u32)(a) | ((__u32)(b) << 8) | ((__u32)© << 16) | ((__u32)(d) << 24))

例如YUYV
#define V4L2_PIX_FMT_YUYV v4l2_fourcc(‘Y’, ‘U’, ‘Y’, ‘V’) /* 16 YUV 4:2:2

例如OV5640所支持的格式
在这里插入图片描述

2、查看或设置当前硬件的图像配置

应用可以通过调用VIDIOC_G_FMT知道硬件现在的配置如何。这种情况下传递的参数是一个v4l2_format 结构体:


                
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值