28、V4L2视频处理:从代码实现到工具应用

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视频处理相关的开发和调试工作。

提供了基于BP(Back Propagation)神经网络结合PID(比例-积分-微分)控制策略的Simulink仿真模型。该模型旨在实现对杨艺所著论文《基于S函数的BP神经网络PID控制器及Simulink仿真》中的理论进行实践验证。在Matlab 2016b环境下开发,经过测试,确保能够正常运行,适合学习和研究神经网络在控制系统中的应用。 特点 集成BP神经网络:模型中集成了BP神经网络用于提升PID控制器的性能,使之能更好地适应复杂控制环境。 PID控制优化:利用神经网络的自学习能力,对传统的PID控制算法进行了智能调整,提高控制精度和稳定性。 S函数应用:展示了如何在Simulink中通过S函数嵌入MATLAB代码实现BP神经网络的定制化逻辑。 兼容性说明:虽然开发于Matlab 2016b,但理论上兼容后续版本,可能会需要调整少量配置以适配不同版本的Matlab。 使用指南 环境要求:确保你的电脑上安装有Matlab 2016b或更高版本。 模型加载: 下载本仓库到本地。 在Matlab中打开.slx文件。 运行仿真: 调整模型参数前,请先熟悉各模块功能和输入输出设置。 运行整个模型,观察控制效果。 参数调整: 用户可以自由调节神经网络的层数、节点数以及PID控制器的参数,探索不同的控制性能。 学习和修改: 通过阅读模型中的注释和查阅相关文献,加深对BP神经网络与PID控制结合的理解。 如需修改S函数内的MATLAB代码,建议有一定的MATLAB编程基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值