V4L2视频处理:从代码实现到工具应用
1. 缓冲区操作
1.1 缓冲区分配
在进行视频数据处理时,首先需要进行缓冲区的分配。以下是一段简单的代码示例,展示了如何分配缓冲区:
buf_addr[0].length = buffer_size;
buf_addr[0].start = malloc(buffer_size);
if (!buf_addr[0].start) {
fprintf(stderr, " Out of memory\n" );
exit(EXIT_FAILURE);
}
这里定义了自定义数据结构
struct buffer_addr
,并为其分配了内存。若内存分配失败,程序将输出错误信息并退出。
1.2 缓冲区入队
在访问缓冲区及其数据之前,需要将缓冲区入队。入队操作使用
VIDIOC_QBUF
ioctl,会将缓冲区的内存页锁定在物理内存中,防止其被交换到磁盘。入队操作分为以下几种情况:
-
用户指针缓冲区入队
:
/* Prime buffers */
for (i = 0; i < BUF_COUNT; ++i) {
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
buf.index = i;
buf.m.userptr = (unsigned long)buf_addr[i].start;
buf.length = buf_addr[i].length;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit(" VIDIOC_QBUF" );
}
- 内存映射缓冲区入队 :
/* Prime buffers */
for (i = 0; i < BUF_COUNT; ++i) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
errno_exit (" VIDIOC_QBUF" );
}
- DMABUF缓冲区入队 :
/* Prime buffers */
for (i = 0; i < BUF_COUNT; ++i) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.index = i;
buf.m.fd = outdev_dmabuf_fd[i];
/* enqueue the dmabuf to capture device */
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
errno_exit (" VIDIOC_QBUF" );
}
1.3 缓冲区出队
缓冲区出队是应用程序读取循环的一部分,使用
VIDIOC_DQBUF
ioctl。以下是不同类型缓冲区出队的示例:
-
内存映射缓冲区出队
:
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
default:
errno_exit (" VIDIOC_DQBUF" );
}
}
/* make sure the returned index is coherent with
the number
* of buffers allocated */
assert (buf.index < BUF_COUNT);
/* We use buf.index to point to the correct
entry in our * buf_addr */
process_image(buf_addr[buf.index].start);
/* Queue back this buffer again, after
processing is done */
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
errno_exit (" VIDIOC_QBUF" );
- 用户指针缓冲区出队 :
struct v4l2_buffer buf;
int i;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_USERPTR;
/* Dequeue a captured buffer */
if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
[...]
default:
errno_exit (" VIDIOC_DQBUF" );
}
}
/*
* We may need the index to which corresponds
this buffer
* in our buf_addr array. This is done by
matching address
* returned by the dequeue ioctl with the one
stored in our
* array */
for (i = 0; i < BUF_COUNT; ++i)
if (buf.m.userptr == (unsigned long)buf_addr[i].start &&
buf.length == buf_addr[i].length)
break;
/* the corresponding index is used for sanity
checks only */
assert (i < BUF_COUNT);
process_image ((void *)buf.m.userptr);
/* requeue the buffer */
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
errno_exit (" VIDIOC_QBUF" );
- 读写I/O方式出队 :
if (-1 == read (fd, buffers[0].start, buffers[0].length)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
[...]
default:
errno_exit (" read" );
}
}
process_image (buffers[0].start);
1.4 启用流
启用流意味着告知V4L2现在将访问输出队列。使用
VIDIOC_STREAMON
来实现这一点,示例代码如下:
/* Start streaming */
int ret;
int a = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = xioctl(capt.fd, VIDIOC_STREAMON, &a);
if (ret < 0) {
perror(" VIDIOC_STREAMON\n" );
return -1;
}
2. V4L2用户空间工具
2.1 v4l2-ctl工具概述
v4l2-ctl
是
v4l-utils
包中的一个用户空间应用程序,可用于查询或配置V4L2设备(包括子设备)。它有助于设置和设计基于V4L2的系统,调整和利用设备的特性。
2.2 工具使用操作
-
列出视频设备及其功能
:
-
使用
--list-devices选项列出所有可用的视频设备:
-
使用
# v4l2-ctl --list-devices
Integrated Camera: Integrated C (usb-0000:00:14.0-8):
/dev/video0
/dev/video1
- 使用`-D`选项获取特定设备的信息:
# v4l2-ctl -d /dev/video0 -D
Driver Info (not using libv4l2):
Driver name : uvcvideo
Card type : Integrated Camera: Integrated C
Bus info : usb-0000:00:14.0-8
Driver version: 5.4.60
Capabilities : 0x84A00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
-
更改设备属性
:
-
使用
-L选项获取设备支持的控制列表:
-
使用
# v4l2-ctl -L
brightness 0x00980900 (int) : min=0 max=255 step=1 default=128 value=128
contrast 0x00980901 (int) : min=0 max=255 step=1 default=32 value=32
saturation 0x00980902 (int) : min=0 max=100 step=1 default=64 value=64
hue 0x00980903 (int) : min=-180 max=180 step=1 default=0 value=0
white_balance_temperature_auto 0x0098090c (bool) : default=1 value=1
gamma 0x00980910 (int) : min=90 max=150 step=1 default=120 value=120
power_line_frequency 0x00980918 (menu) : min=0 max=2 default=1 value=1
0: Disabled
1: 50 Hz
2: 60 Hz
white_balance_temperature 0x0098091a (int) : min=2800 max=6500 step=1 default=4600 value=4600 flags=inactive
sharpness 0x0098091b (int) : min=0 max=7 step=1 default=3 value=3
backlight_compensation 0x0098091c (int) : min=0 max=2 step=1 default=1 value=1
exposure_auto 0x009a0901 (menu) : min=0 max=3 default=3 value=3
1: Manual Mode
3: Aperture Priority Mode
exposure_absolute 0x009a0902 (int) : min=5 max=1250 step=1 default=157 value=157 flags=inactive
exposure_auto_priority 0x009a0903 (bool) : default=0 value=1
- 使用`--set-ctrl`选项更改控制值:
# v4l2-ctl --set-ctrl brightness=192
- 使用`--get-ctrl`选项检查当前控制值:
# v4l2-ctl --get-ctrl brightness
brightness: 192
-
设置像素格式、分辨率和帧率
:
-
使用
--list-formats-ext选项获取设备支持的像素格式、分辨率和帧率:
-
使用
# v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: ' MJPG' (compressed)
Name : Motion-JPEG
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 960x540
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 848x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 640x360
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 424x240
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 352x288
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x180
Interval: Discrete 0.033s (30.000 fps)
Index : 1
Type : Video Capture
Pixel Format: ' YUYV'
Name : YUYV 4:2:2
Size: Discrete 1280x720
Interval: Discrete 0.100s (10.000 fps)
Size: Discrete 960x540
Interval: Discrete 0.067s (15.000 fps)
Size: Discrete 848x480
Interval: Discrete 0.050s (20.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 640x360
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 424x240
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 352x288
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x240
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 320x180
Interval: Discrete 0.033s (30.000 fps)
- 使用`--set-parm`选项设置帧率:
# v4l2-ctl --set-parm=30
Frame rate set to 30.000 fps
- 使用`--set-fmt-video`选项设置分辨率和像素格式:
# v4l2-ctl --set-fmt-video=width=640,height=480, pixelformat=MJPG
2.3 捕获帧和流
v4l2-ctl
支持许多选项,可通过以下帮助命令查看相关选项:
-
--help-streaming
:打印处理流的所有选项的帮助信息。
-
--help-subdev
:打印处理
v4l-subdevX
设备的所有选项的帮助信息。
-
--help-vidcap
:打印获取/设置/列出视频捕获格式的所有选项的帮助信息。
以下是捕获帧的示例命令:
- 捕获QVGA MJPG压缩帧:
# v4l2-ctl --set-fmt-video=width=320,height=240, pixelformat=MJPG \
--stream-mmap --stream-count=1 --stream-to=grab-320x240.mjpg
- 捕获原始YUV图像:
# v4l2-ctl --set-fmt-video=width=320,height=240, pixelformat=YUYV \
--stream-mmap --stream-count=1 --stream-to=grab-320x240-yuyv.raw
2.4 工具操作流程
graph LR
A[列出设备] --> B[获取设备信息]
B --> C[获取控制列表]
C --> D[更改控制值]
D --> E[获取支持格式]
E --> F[设置帧率]
F --> G[设置分辨率和格式]
G --> H[捕获帧]
通过以上操作,我们可以利用
v4l2-ctl
工具对V4L2设备进行全面的配置和测试。
3. V4L2调试与合规性测试
3.1 V4L2用户空间调试
由于视频系统设置可能存在错误,V4L2提供了从用户空间进行调试的方法,以追踪和解决来自V4L2框架核心或用户空间API的问题。
-
启用框架调试
:
# echo 0x3 > /sys/module/videobuf2_v4l2/parameters/debug
# echo 0x3 > /sys/module/videobuf2_common/parameters/debug
这两条命令会指示V4L2将核心跟踪信息添加到内核日志消息中,方便追踪问题来源。之后可以使用
dmesg
命令查看日志:
# dmesg
[831707.512821] videobuf2_common: __setup_offsets: buffer 0, plane 0 offset 0x00000000
[831707.512915] videobuf2_common: __setup_offsets: buffer 1, plane 0 offset 0x00097000
[831707.513003] videobuf2_common: __setup_offsets: buffer 2, plane 0 offset 0x0012e000
[831707.513118] videobuf2_common: __setup_offsets: buffer 3, plane 0 offset 0x001c5000
[831707.513119] videobuf2_common: __vb2_queue_alloc: allocated 4 buffers, 1 plane(s) each
[831707.513169] videobuf2_common: vb2_mmap: buffer 0, plane 0 successfully mapped
[831707.513176] videobuf2_common: vb2_core_qbuf: qbuf of buffer 0 succeeded
[831707.513205] videobuf2_common: vb2_mmap: buffer 1, plane 0 successfully mapped
[831707.513208] videobuf2_common: vb2_core_qbuf: qbuf of buffer 1 succeeded
[...]
- 启用用户空间API跟踪 :
$ echo 0x3 > /sys/class/video4linux/video0/dev_debug
执行捕获原始图像的命令后,再次使用
dmesg
命令查看日志,可跟踪不同的V4L2用户空间API调用:
$ dmesg
[833211.742260] video0: VIDIOC_QUERYCAP: driver=uvcvideo, card=Integrated Camera: Integrated C, bus=usb-0000:00:14.0-8, version=0x0005043c, capabilities=0x84a00001, device_caps=0x04200001
[833211.742275] video0: VIDIOC_QUERY_EXT_CTRL: id=0x980900, type=1, name=Brightness, min/max=0/255, step=1, default=128, flags=0x00000000, elem_size=4, elems=1, nr_of_dims=0, dims=0,0,0,0
[...]
[833211.742318] video0: VIDIOC_QUERY_EXT_CTRL: id=0x98090c, type=2, name=White Balance Temperature, Auto, min/max=0/1, step=1, default=1, flags=0x00000000, elem_size=4, elems=1, nr_of_dims=0, dims=0,0,0,0
[833211.742365] video0: VIDIOC_QUERY_EXT_CTRL: id=0x98091c, type=1, name=Backlight Compensation, min/max=0/2, step=1, default=1, flags=0x00000000, elem_size=4, elems=1, nr_of_dims=0, dims=0,0,0,0
[833211.742376] video0: VIDIOC_QUERY_EXT_CTRL: id=0x9a0901, type=3, name=Exposure, Auto, min/max=0/3, step=1, default=3, flags=0x00000000, elem_size=4, elems=1, nr_of_dims=0, dims=0,0,0,0
[...]
[833211.756641] videobuf2_common: vb2_mmap: buffer 1, plane 0 successfully mapped
[833211.756646] videobuf2_common: vb2_core_qbuf: qbuf of buffer 1 succeeded
[833211.756649] video0: VIDIOC_QUERYBUF: 00:00:00.00000000 index=2, type=vid-cap, request_fd=0, flags=0x00012000, field=any, sequence=0, memory=mmap, bytesused=0, offset/userptr=0x12e000, length=614989
[833211.756657] timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
[833211.756698] videobuf2_common: vb2_mmap: buffer 2, plane 0 successfully mapped
[833211.756704] videobuf2_common: vb2_core_qbuf: qbuf of buffer 2 succeeded
[833211.756706] video0: VIDIOC_QUERYBUF: 00:00:00.00000000 index=3, type=vid-cap, request_fd=0, flags=0x00012000, field=any, sequence=0, memory=mmap, bytesused=0, offset/userptr=0x1c5000, length=614989
[833211.756714] timecode=00:00:00 type=0, flags=0x00000000, frames=0, userbits=0x00000000
[833211.756751] videobuf2_common: vb2_mmap: buffer 3, plane 0 successfully mapped
[833211.756755] videobuf2_common: vb2_core_qbuf: qbuf of buffer 3 succeeded
[833212.967229] videobuf2_common: vb2_core_streamon: successful
[833212.967234] video0: VIDIOC_STREAMON: type=vid-cap
3.2 V4L2合规性驱动测试
为了使驱动程序符合V4L2标准,必须通过
v4l2-compliance
工具的测试。该工具用于测试各种V4L设备,几乎涵盖了所有V4L2的ioctl。
-
测试命令
:
# v4l2-compliance
若不指定设备,默认测试
/dev/video0
。以下是部分输出示例:
v4l2-compliance SHA : not available
Driver Info:
Driver name : uvcvideo
Card type : Integrated Camera: Integrated C
Bus info : usb-0000:00:14.0-8
Driver version: 5.4.60
Capabilities : 0x84A00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
Compliance test for device /dev/video0 (not using libv4l2):
Required ioctls:
test VIDIOC_QUERYCAP: OK
Allow for multiple opens:
test second video open: OK
test VIDIOC_QUERYCAP: OK
test VIDIOC_G/S_PRIORITY: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
test VIDIOC_LOG_STATUS: OK (Not Supported)
[]
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
[...]
Test input 0:
Control ioctls:
fail: v4l2-test-controls.cpp(214): missing control class for class 00980000
fail: v4l2-test-controls.cpp(251): missing control class for class 009a0000
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: FAIL
test VIDIOC_QUERYCTRL: OK
fail: v4l2-test-controls.cpp(437): s_ctrl returned an error (84)
test VIDIOC_G/S_CTRL: FAIL
fail: v4l2-test-controls.cpp(675): s_ext_ctrls returned an error (
从输出中可以看到设备的驱动信息、功能以及各项测试的结果。使用
--verbose
命令可以使输出更详细、更友好。
3.3 测试操作流程
graph LR
A[启动框架调试] --> B[查看内核日志]
B --> C[启动用户空间API跟踪]
C --> D[执行捕获操作]
D --> E[再次查看内核日志]
E --> F[运行合规性测试]
F --> G[查看测试结果]
4. 总结
本文围绕V4L2视频处理展开,详细介绍了缓冲区操作、用户空间工具的使用、调试方法以及合规性测试。
4.1 缓冲区操作总结
| 操作类型 | 描述 |
|---|---|
| 缓冲区分配 | 为自定义数据结构分配内存,失败时输出错误信息并退出 |
| 缓冲区入队 | 根据不同类型(用户指针、内存映射、DMABUF)进行入队操作,锁定内存页 |
| 缓冲区出队 | 分为内存映射、用户指针、读写I/O三种方式,出队后处理数据并重新入队 |
| 启用流 |
使用
VIDIOC_STREAMON
告知V4L2访问输出队列
|
4.2 工具与测试总结
- v4l2-ctl工具 :可对V4L2设备进行查询、配置、捕获帧等操作,通过一系列选项完成设备的全面管理。
-
调试方法
:通过向特定文件写入值启用框架调试和用户空间API跟踪,利用
dmesg查看日志。 -
合规性测试
:使用
v4l2-compliance工具测试驱动程序是否符合V4L2标准。
通过掌握这些知识和技能,开发者可以更好地进行V4L2视频处理相关的开发和调试工作。
超级会员免费看
2561

被折叠的 条评论
为什么被折叠?



