v4l2编程
一、v4l2基础
V4l2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。
经典例程:https://linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/v4l2spec/capture.c
二、v4l2工具
常用的命令行工具v4l2-ctl在包v4l-utils中,可通过sudo apt install v4l-utils安装。
# 通过v4l2查看摄像头设备
$ sudo v4l2-ctl --list-devices
# 查看当前摄像头支持的视频格式、分辨率和帧速率
$ sudo v4l2-ctl -d /dev/video0 --list-formats
$ sudo v4l2-ctl -d /dev/video0 --list-formats-ext
# 查看摄像头所有参数
$ sudo v4l2-ctl -d /dev/video0 --all
# 查看摄像头所支持的分辨率
sudo v4l2-ctl --list-framesizes=MJPG -d /dev/video0
#设置
v4l2-ctl --set-parm=30
v4l2-ctl --set-fmt-video=width=320,height=240,pixelformat=YUYV
$ v4l2-ctl help unknown arguments: help General/Common options: --all display all information available -C, --get-ctrl=<ctrl>[,<ctrl>...] get the value of the controls [VIDIOC_G_EXT_CTRLS] -c, --set-ctrl=<ctrl>=<val>[,<ctrl>=<val>...] set the value of the controls [VIDIOC_S_EXT_CTRLS] -D, --info show driver info [VIDIOC_QUERYCAP] -d, --device=<dev> use device <dev> instead of /dev/video0 if <dev> starts with a digit, then /dev/video<dev> is used -e, --out-device=<dev> use device <dev> for output streams instead of the default device as set with --device if <dev> starts with a digit, then /dev/video<dev> is used -h, --help display this help message --help-all all options --help-io input/output options --help-misc miscellaneous options --help-overlay overlay format options --help-sdr SDR format options --help-selection crop/selection options --help-stds standards and other video timings options --help-streaming streaming options --help-tuner tuner/modulator options --help-vbi VBI format options --help-vidcap video capture format options --help-vidout vidout output format options --help-edid edid handling options -k, --concise be more concise if possible. -l, --list-ctrls display all controls and their values [VIDIOC_QUERYCTRL] -L, --list-ctrls-menus display all controls and their menus [VIDIOC_QUERYMENU] -r, --subset=<ctrl>[,<offset>,<size>]+ the subset of the N-dimensional array to get/set for control <ctrl>, for every dimension an (<offset>, <size>) tuple is given. -w, --wrapper use the libv4l2 wrapper library. --list-devices list all v4l devices --log-status log the board status in the kernel log [VIDIOC_LOG_STATUS] --get-priority query the current access priority [VIDIOC_G_PRIORITY] --set-priority=<prio> set the new access priority [VIDIOC_S_PRIORITY] <prio> is 1 (background), 2 (interactive) or 3 (record) --silent only set the result code, do not print any messages --sleep=<secs> sleep <secs>, call QUERYCAP and close the file handle --verbose turn on verbose ioctl status reporting
三、常用数据结构
常用的IOCTL接口命令在include/linux/videodev2.h中定义
VIDIOC_REQBUFS //向设备申请缓存区 VIDIOC_QUERYBUF //获取缓存帧地址、长度(把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址) VIDIOC_QUERYCAP //查询驱动功能 VIDIOC_ENUM_FMT //获取当前驱动支持的视频格式 VIDIOC_S_FMT //设置当前驱动的视频捕获格式 VIDIOC_G_FMT //读取当前驱动的视频捕获格式 VIDIOC_TRY_FMT //验证当前驱动的显示格式 VIDIOC_ENUM_FRAMESIZES //枚举设备支持的分辨率信息 VIDIOC_ENUM_FRAMEINTERVALS //获取设备支持的帧间隔 VIDIOC_G_PARM //获取视频流参数(频率等) VIDIOC_S_PARM //设定视频流参数(频率等) VIDIOC_CROPCAP //查询驱动的修剪能力 VIDIOC_S_CROP //设置视频信号的矩形边框 VIDIOC_G_CROP //读取视频信号的矩形边框 VIDIOC_QBUF //把数据从缓存中读取出来 VIDIOC_DQBUF //把数据放回缓存队列 VIDIOC_STREAMON //开始视频显示函数 VIDIOC_STREAMOFF //结束视频显示函数 VIDIOC_QUERYSTD //检查当前视频设备支持的标准,例如PAL或NTSC。
常用的结构体在系统目录/usr/include/linux/videodev2.h。
struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS memory-mapping buffers 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_fmtdesc //帧的格式描述,对应命令VIDIOC_ENUM_FMT等 struct v4l2_buffer //驱动中的一帧图像缓存信息,对应命令VIDIOC_QUERYBUF struct v4l2_crop //视频信号矩形边框 struct v4l2_cropcap //设置摄像头的捕捉能力,在捕捉视频时应先设置 typedef __u64 v4l2_std_id //视频制式
重要数据结构详情
/* * 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]; /* 格式名称描述 */ __u32 pixelformat; /* Format fourcc,格式*/ __u32 reserved[4]; }; #define V4L2_FMT_FLAG_COMPRESSED 0x0001 #define V4L2_FMT_FLAG_EMULATED 0x0002
所有的视频格式可以能下面的方法查看
#define v4l2_fourcc(a, b, c, d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
/* * V I D E O I M A G E F O R M A T */ struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; //帧格式 V4L2_PIX_FMT_MJPEG V4L2_PIX_FMT_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_*) */ 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 */ }; /** * struct v4l2_format - stream data format * @type: enum v4l2_buf_type; type of the data stream * @pix: definition of an image format * @pix_mp: definition of a multiplanar image format * @win: definition of an overlaid image * @vbi: raw VBI capture or output parameters * @sliced: sliced VBI capture or output parameters * @raw_data: placeholder for future extensions and custom formats */ 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; };
/** * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP * * @driver: name of the driver module (e.g. "bttv") * @card: name of the card (e.g. "Hauppauge WinTV") * @bus_info: name of the bus (e.g. "PCI:" + pci_name(pci_dev) ) * @version: KERNEL_VERSION * @capabilities: capabilities of the physical device as a whole * @device_caps: capabilities accessed via this particular device (node) * @reserved: reserved fields for future extensions */ struct v4l2_capability { __u8 driver[16]; //驱动名 __u8 card[32]; //设备名 __u8 bus_info[32]; //设备在系统中的位置 __u32 version; //驱动版本号 __u32 capabilities; //支持的操作 V4L2_CAP_VIDEO_CAPTURE, V4L2_CAP_STREAMING __u32 device_caps; //设备能力 __u32 reserved[3]; };
/* * M E M O R Y - M A P P I N G B U F F E R S */ struct v4l2_requestbuffers { __u32 count; __u32 type; /* enum v4l2_buf_type */ __u32 memory; /* enum v4l2_memory */ __u32 reserved[2]; }; /** * struct v4l2_buffer - video buffer info * @index: id number of the buffer * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for * multiplanar buffers); * @bytesused: number of bytes occupied by data in the buffer (payload); * unused (set to 0) for multiplanar buffers * @flags: buffer informational flags * @field: enum v4l2_field; field order of the image in the buffer * @timestamp: frame timestamp * @timecode: frame timecode * @sequence: sequence count of this frame * @memory: enum v4l2_memory; the method, in which the actual video data is * passed * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; * offset from the start of the device memory for this plane, * (or a "cookie" that should be passed to mmap() as offset) * @userptr: for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR; * a userspace pointer pointing to this buffer * @fd: for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF; * a userspace file descriptor associated with this buffer * @planes: for multiplanar buffers; userspace pointer to the array of plane * info structs for this buffer * @length: size in bytes of the buffer (NOT its payload) for single-plane * buffers (when type != *_MPLANE); number of elements in the * planes array for multi-plane buffers * * Contains data exchanged by application and driver using one of the Streaming * I/O methods. */ 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; };
视频流参数
struct v4l2_fract { __u32 numerator; __u32 denominator; }; 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]; }; /* Flags for 'capability' and 'capturemode' fields */ #define V4L2_MODE_HIGHQUALITY 0x0001 /* High quality imaging mode */ #define V4L2_CAP_TIMEPERFRAME 0x1000 /* timeperframe field is supported */ struct v4l2_outputparm { __u32 capability; /* Supported modes */ __u32 outputmode; /* Current mode */ struct v4l2_fract timeperframe; /* Time per frame in seconds */ __u32 extendedmode; /* Driver-specific extensions */ __u32 writebuffers; /* # of buffers for write */ __u32 reserved[4]; }; /* Stream type-dependent parameters */ 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; };
帧分辨率
VIDIOC_ENUM_FRAMESIZES:针对VIDIOC_ENUM_FMT所列出的图像格式信息,列举某一格式所对应的分辨率信息。
v4l2_frmsizeenum->type可能有三种情况:
V4L2_FRMSIZE_TYPE_DISCRETE: 可以递增的设置v4l2_frmsizeenum->index来重复调用直到返回EINVAL来获取该种图像格式下所有支持的分辨率. 此时, 可以通过v4l2_frmsizeenum->width和height来获取支持的分辨率的长宽。
V4L2_FRMSIZE_TYPE_STEPWISE: 此时只有v4l2_frmsizeenum->stepwise是有效的, 并且不能再将index设为其他值重复调用此ioctl。
V4L2_FRMSIZE_TYPE_CONTINUOUS: STEPWISE的一种特殊情况, 此时同样只有stepwise有效, 并且stepwise.step_width和stepwise.step_height都为1。
int ioctl( int fd, int request, struct v4l2_frmsizeenum *arg); struct v4l2_frmsizeenum { __u32 index; //IN:index of the given frame size in the enumeration __u32 pixel_format; //IN:pixel format for which the frame sizes are enumerated __u32 type; //OUT: frame size type the device supports union //OUT: frame size with the given index { struct v4l2_frmsize_discrete discrete; struct v4l2_frmsize_stepwise stepwise; }; __u32 reserved[2]; }; struct v4l2_frmsize_discrete { __u32 width; __u32 height; }; 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] };
Video帧
Video的帧可区分为隔行和逐行:逐行顺序的传输一帧所有的行,而隔行则把一帧划分成两个fields,分别保存帧的奇数行和偶数行,被称作奇场和偶场。
所有的video capture和out devices必须汇报当前的field顺序。一些驱动可能允许选择不同的序,终端应用可以在调用VIDIOC_S_FMT前初始化struct v4l2_pix_format的field成员,否则可以使用V4L2_FIELD_ANY。
V4L2_FIELD_ANY |
0 |
Application 可以请求使用这个参数,如果V4L2_FIELD_NONE, V4L2_FIELD_TOP, V4L2_FIELD_BOTTOM,V4L2_FIELD_INTERLACE 中任何一个格式都支持.驱动选择使用哪一个格式依赖于硬件能力,以及请求的image尺寸,驱动选择一个然后返回这个格式。struct_buffer的field成员不可以为V4L2_FIELD_ANY。 |
V4L2_FIELD_NONE |
1 |
Images是逐行格式,当驱动无法区分V4L2_FIELD_TOP和V4L2_FIELD_BOTTOM,可以使用这种field类型 |
V4L2_FIELD_TOP |
2 |
Images仅仅包含top field |
V4L2_FIELD_BOTTOM |
3 |
Images仅仅包含bottom field,应用可能希望防止设备捕获interlaced的图片,因为这种图片会在运动物体周围产生毛状特效 |
V4L2_FIELD_INTERLACED |
4 |
Images包含top和bottom field, 隔行交替,场序依赖于当前video的标准。NTSC首先传输bottom field, PAL则先传输top field。 |
V4L2_FIELD_SEQ_TB |
5 |
Images包含top和bottom field, top field的行首先存放在memory中,然后紧跟着bottom field的行。 Fields一直以瞬间序存储,较老的放在内存前面。Images的尺寸和帧相关,而不是field
|
V4L2_FIELD_SEQ_BT |
6 |
Images包含top和bottom field, bottom field的行首先存放在memory中,然后紧跟着top field的行。 Fields一直以瞬间序存储,较老的放在内存前面。Images的尺寸和帧相关,而不是field |
V4L2_FIELD_ALTERATE |
7 |
一个帧的两个field分别放在不同的buffer, 按照瞬间序,也就是说老的一个是第一个。driver或者应用指明field的奇偶性(奇偶性:当前的field是top 还是bottom field). 任何两个连续的field构成一个frame,是否两个field是连续的,不需要drop掉他们,可以通过v4l2_buffer中的sequence 成员判定。Images的尺寸和frame相关而不是fields相关 |
V4L2_FIELD_INTERLACED_TB |
8 |
Images 包含top和bottom field, 每行交替, top field在前面。top field首先传送 |
V4L2_FIELD_INTERLACED_BT |
9 |
Images 包含top和bottom field, 每行交替, bottom field在前面。bottom field首先传送 |
四、采集流程
打开设备->获取和设置设备属性->设置输出输入->(申请缓冲及缓冲管理)->获取数据->关闭设备。
注:各类VIDIOC命令相关的ioctl接口函数返回值 ret,若小于零,则表示函数调用失败,若等于零,表示成功执行。
应用程序和设备有三种交换数据的方法,直接read/write、内存映射(mmap)和用户指针,一般采用内存映射方式。
- Open设备文件;
- 获取设备能力:VIDIOC_QUERYCAP;
- 枚举设备支持的视频格式:VIDIOC_ENUM_FMT;
- 设置捕获视频格式:VIDIOC_S_FMT,VIDIOC_G_FMT;
- 设置视频流信息:VIDIOC_S_PARM,VIDIOC_G_PARM;
- 向驱动申请帧缓存,一般不超过5个:VIDIOC_REQBUFS;申请的帧缓存位于内核空间,应用程序需要mmap访问。
- 获取每个缓存信息并映射(mmap)到用户空间:VIDIOC_QUERYBUF;将所有缓存放入视频采集队列:VIDIOC_QBUF。
- 开启视频采集:VIDIOC_STREAMON;
- 取出帧缓存:VIDIOC_DQBUF;处理数据;把处理后的帧缓存放入队列尾:VIDIOC_QBUF;如此循环。
- 停止采集:VIDIOC_STREAMOFF;
- 取出帧缓存:VIDIOC_DQBUF;解除映射munmap;关闭设备。
注:在驱动程序处理视频的过程中,定义了两个队列:视频采集输入队列(incoming queues)和视频采集输出队列(outgoing queues),前者是等待驱动存放视频数据的队列,后者是驱动程序已经放入了视频数据的队列。
参考:
1. https://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html 官方API
2. https://linuxtv.org/downloads/v4l-dvb-apis/index.html
3. V4L2采集视频https://notes.z-dd.net/
4. V4L2开发应用流程的各类超实用VIDIOC命令及其结构体集锦
5. (原创)基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集 超群天晴
6. Uvc设备
7. v4l2_field理解
8. Linux USB摄像头使用简书
9. 基于v4l2的视频驱动开发华清远见
10. v4l2的学习建议和流程解析
11. 视频编码 v4l2 摄像头 图像处理 Biao csdn li_wen01