命令介绍
本文档说明 esp_video 已实现的 V4L2 ioctl 请求代码、命令参数及用法示例,便于按功能查阅。设备节点、Capture / M2M 数据通路以及应用侧典型调用顺序见 ESP Video 应用开发指南。初始化 API 见 API 参考。
重要
示例侧重接口用法,未覆盖完整错误处理。产品代码须检查
ioctl返回值。组件只实现当前芯片硬件能力范围内的 V4L2 功能。未列入下表的标准命令默认未实现;部分命令仅部分设备可用,或以编译宏为准。例如
VIDIOC_SUBSCRIBE_EVENT/VIDIOC_DQEVENT/VIDIOC_S_EVENT_CALLBACK仅在 MIPI-CSI 驱动支持事件时可用(ESP_VIDEO_CSI_DRIVER_HAS_EVENT)。若当前 ESP-IDF 的 MIPI-CSI 驱动不含错误事件,应用程序不应调用这些命令。Capture 设备使用
V4L2_BUF_TYPE_VIDEO_CAPTURE。M2M 编解码设备(JPEG 编码/dev/video10、H.264 编码/dev/video11、JPEG 解码/dev/video12)须分别对V4L2_BUF_TYPE_VIDEO_OUTPUT``(输入)和 ``V4L2_BUF_TYPE_VIDEO_CAPTURE``(输出)申请缓冲并 ``STREAMON/STREAMOFF。像素格式按字节序区分:YUV422 使用
V4L2_PIX_FMT_YUYV/V4L2_PIX_FMT_UYVY;RGB565 使用V4L2_PIX_FMT_RGB565``(小端)/ ``V4L2_PIX_FMT_RGB565X``(大端)。已不再使用 ``V4L2_PIX_FMT_YUV422P。JPEG 解码器还支持V4L2_PIX_FMT_BGR565。DVP 在支持格式转换时,仅当传感器输出为 YUV422 YUYV 时可将数据转为 RGB565,转换结果是大端V4L2_PIX_FMT_RGB565X,不是小端V4L2_PIX_FMT_RGB565。传感器输出 UYVY 时不支持该转换。
ioctl 调用顺序
应用侧典型顺序与 ESP Video 应用开发指南 中的应用编程一节一致:打开设备 → 查询能力与格式 → 申请并排队缓冲区 → STREAMON → 循环 DQBUF / QBUF → STREAMOFF → 关闭设备。ioctl 会落到对应子设备;以查询、设置相机传感器格式为例:

ioctl 调用流程
下文先给出已实现命令一览,再按功能展开参数与示例。超时、传感器格式、事件等厂商扩展 ioctl 见后文扩展命令。控制项 ID 见文末控制 ID 一览。
V4L2 命令
当前已实现的请求代码如下。未列出的标准命令默认未实现。
命令 |
适用范围 |
|---|---|
|
所有视频设备 |
|
Capture 与 M2M;须在停流时设置格式 |
|
CSI / DVP / SPI 等采集设备 |
|
所有流式设备; |
|
所有流式设备;M2M 需分别启停 OUTPUT 与 CAPTURE |
|
传感器、ISP、JPEG、H.264 等控制项 |
|
MIPI-CSI 裁剪 |
|
采集帧间隔;H.264 设备可用于配置/查询 FPS(开流后也可设置) |
|
相机传感器输出格式:设置 / 获取 / 枚举 |
|
自动对焦电机(需使能电机控制器) |
|
增减设备引用计数 |
|
|
|
仅 MIPI-CSI,且驱动支持事件 |
|
须在开流之后、停流之前调用 |
上表同时列出标准命令与厂商扩展命令。下面按查询、格式、缓冲、数据流、控制的顺序说明标准命令;每小节依次给出请求代码、参数结构、字段说明和示例。厂商扩展命令见后文扩展命令。
查询设备能力
命令简介:
命令(请求代码):
VIDIOC_QUERYCAP命令参数:
struct v4l2_capability
参数主要字段:
参数 |
类型 |
用途 |
|---|---|---|
driver |
__u8[16] |
驱动名 |
card |
__u8[32] |
硬件卡名 |
bus_info |
__u8[32] |
总线信息 |
version |
__u32 |
驱动版本 |
capabilities |
__u32 |
能力位掩码 |
device_caps |
__u32 |
设备能力位掩码 |
用法示例:
void example_querycap(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_capability cap = {0};
ioctl(fd, VIDIOC_QUERYCAP, &cap);
printf("driver: %s, card: %s, bus: %s\n", (char *)cap.driver, (char *)cap.card, (char *)cap.bus_info);
printf("version: %d.%d.%d\n", (uint16_t)(cap.version >> 16),
(uint8_t)(cap.version >> 8),
(uint8_t)cap.version);
printf("capabilities: 0x%x\n", cap.capabilities);
printf("device_caps: 0x%x\n", cap.device_caps);
close(fd);
}
枚举像素格式
命令简介:
命令(请求代码):
VIDIOC_ENUM_FMT命令参数:
struct v4l2_fmtdesc
参数主要字段:
参数 |
类型 |
用途 |
|---|---|---|
index |
__u32 |
格式序号递增 |
type |
__u32 |
缓冲区类型 |
description |
__u8[32] |
格式描述 |
pixelformat |
__u32 |
四字符像素码 |
用法示例:
void example_enum_fmt(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_fmtdesc fmtdesc = {0};
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for (fmtdesc.index = 0; ; ++fmtdesc.index) {
if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0) {
break;
}
printf("Format: %s, pixelformat: 0x%x\n", (char *)fmtdesc.description, fmtdesc.pixelformat);
}
close(fd);
}
设置/获取数据格式
命令简介:
命令(请求代码):
VIDIOC_S_FMT/VIDIOC_G_FMT命令参数:
struct v4l2_format
参数主要字段(以 type == V4L2_BUF_TYPE_VIDEO_CAPTURE 为例):
参数 |
类型 |
用途 |
|---|---|---|
type |
__u32 |
缓冲区类型 |
fmt |
union |
格式信息,仅支持 pix 字段 |
pix(struct v4l2_pix_format) 字段说明:
参数 |
类型 |
用途 |
|---|---|---|
width |
__u32 |
图像宽度 |
height |
__u32 |
图像高度 |
pixelformat |
__u32 |
像素格式 |
sizeimage |
__u32 |
图像大小 |
ycbcr_enc |
__u32 |
Y’CbCr 编码 |
quantization |
__u32 |
量化 |
用法示例(设置/获取/尝试格式):
void example_set_get_try_fmt(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_format fmt = {0};
// 设置格式
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 320;
fmt.fmt.pix.height = 240;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
ioctl(fd, VIDIOC_S_FMT, &fmt);
/* 运行时改格式须在停流状态下进行 */
// 获取当前格式
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_G_FMT, &fmt);
printf("Current: %ux%u, fmt=0x%x\n", fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.pixelformat);
close(fd);
}
枚举支持的分辨率
命令简介:
命令(请求代码):
VIDIOC_ENUM_FRAMESIZES命令参数:
struct v4l2_frmsizeenum
参数主要字段:
参数 |
类型 |
用途 |
|---|---|---|
index |
__u32 |
分辨率序号递增 |
pixel_format |
__u32 |
目标像素格式 |
type |
__u32 |
固定/步进/自定义 |
discrete/stepwise |
union |
分辨率信息 |
discrete(struct v4l2_frmsize_discrete) 字段说明:
参数 |
类型 |
用途 |
|---|---|---|
width |
__u32 |
宽度 |
height |
__u32 |
高度 |
stepwise(struct v4l2_frmsize_stepwise) 字段说明:
参数 |
类型 |
用途 |
|---|---|---|
min_width |
__u32 |
最小宽度 |
max_width |
__u32 |
最大宽度 |
step_width |
__u32 |
宽度步长 |
min_height |
__u32 |
最小高度 |
max_height |
__u32 |
最大高度 |
step_height |
__u32 |
高度步长 |
用法示例:
void example_enum_framesizes(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_frmsizeenum fsize = {0};
fsize.pixel_format = V4L2_PIX_FMT_RGB565;
for (fsize.index = 0; ; fsize.index++) {
if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize) < 0) {
break;
}
if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
printf("Discrete: %ux%u\n", fsize.discrete.width, fsize.discrete.height);
} else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
printf("Stepwise: %ux%u~%ux%u, step: %ux%u\n", fsize.stepwise.min_width, fsize.stepwise.min_height, fsize.stepwise.max_width, fsize.stepwise.max_height, fsize.stepwise.step_width, fsize.stepwise.step_height);
}
}
close(fd);
}
枚举帧率
命令简介:
命令(请求代码):
VIDIOC_ENUM_FRAMEINTERVALS命令参数:
struct v4l2_frmivalenum
参数主要字段:
参数 |
类型 |
用途 |
|---|---|---|
index |
__u32 |
帧率序号递增 |
pixel_format |
__u32 |
目标像素格式 |
width |
__u32 |
宽度 |
height |
__u32 |
高度 |
type |
__u32 |
离散/步进/连续 |
discrete/stepwise |
union |
帧率信息 |
discrete(struct v4l2_fract) 字段说明:
参数 |
类型 |
用途 |
|---|---|---|
numerator |
__u32 |
分子 |
denominator |
__u32 |
分母 |
stepwise(struct v4l2_frmival_stepwise) 字段说明:
参数 |
类型 |
用途 |
|---|---|---|
min |
struct v4l2_fract |
最小帧率 |
max |
struct v4l2_fract |
最大帧率 |
step |
struct v4l2_fract |
帧率步长 |
用法示例:
void example_enum_frameintervals(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_frmivalenum ival = {0};
ival.pixel_format = V4L2_PIX_FMT_RGB565;
ival.width = 640;
ival.height = 480;
for (ival.index = 0; ; ival.index++) {
if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &ival) < 0) {
break;
}
if (ival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
printf("Discrete interval: %u/%u\n", ival.discrete.numerator, ival.discrete.denominator);
} else if (ival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
printf("Stepwise interval: %u/%u~%u/%u, step: %u/%u\n", ival.stepwise.min.numerator, ival.stepwise.min.denominator, ival.stepwise.max.numerator, ival.stepwise.max.denominator, ival.stepwise.step.numerator, ival.stepwise.step.denominator);
}
}
close(fd);
}
申请/释放缓冲区
命令(请求代码):
VIDIOC_REQBUFS命令参数:
struct v4l2_requestbuffers
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
count |
__u32 |
申请/释放的缓冲区数量 |
type |
__u32 |
缓冲区类型 |
memory |
__u32 |
缓冲区内存类型 |
用法示例:
void example_reqbufs(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_requestbuffers req = {0};
req.count = 3;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_REQBUFS, &req);
/* count=0 释放已申请的视频缓冲 */
req.count = 0;
ioctl(fd, VIDIOC_REQBUFS, &req);
close(fd);
}
备注
内存类型支持 V4L2_MEMORY_MMAP 与 V4L2_MEMORY_USERPTR。M2M 设备需分别对 V4L2_BUF_TYPE_VIDEO_OUTPUT 与 V4L2_BUF_TYPE_VIDEO_CAPTURE 调用 VIDIOC_REQBUFS。
查询缓冲区属性
命令(请求代码):
VIDIOC_QUERYBUF命令参数:
struct v4l2_buffer
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
index |
__u32 |
缓冲区索引(0 ~ count-1) |
type |
__u32 |
缓冲区类型 |
bytesused |
__u32 |
实际有效数据长度 |
memory |
__u32 |
内存类型 |
m.offset/m.userptr |
union |
缓冲区地址(offset 或 userptr) |
length |
__u32 |
缓冲区总长度 |
用法示例:
void example_querybuf(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_buffer buf = {0};
buf.index = 0;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_QUERYBUF, &buf);
printf("Buffer length: %u\n", buf.length);
printf("Buffer offset: %u\n", buf.m.offset);
close(fd);
}
缓冲区入队列
命令(请求代码):
VIDIOC_QBUF命令参数:
struct v4l2_buffer
结构体字段与 VIDIOC_QUERYBUF 相同。
用法示例:
void example_qbuf(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_buffer buf = {0};
buf.index = 0;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_QBUF, &buf);
close(fd);
}
缓冲区出队列
命令(请求代码):
VIDIOC_DQBUF命令参数:
struct v4l2_buffer
结构体字段与 VIDIOC_QUERYBUF 相同。
用法示例:
void example_dqbuf(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_buffer buf = {0};
buf.index = 0;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(fd, VIDIOC_DQBUF, &buf);
close(fd);
}
启动/停止数据流
命令(请求代码):
VIDIOC_STREAMON/VIDIOC_STREAMOFF命令参数:
int *
功能说明:
STREAMON:开流,开始处理缓冲队列STREAMOFF:停流;设备会丢弃队列中尚未处理的缓冲
M2M 编解码设备须分别对 OUTPUT 与 CAPTURE 调用本命令。USB UVC 在停流后会回收队列中的缓冲。
用法示例:
void example_streamon_streamoff(void)
{
int fd = open("/dev/video0", O_RDWR);
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMOFF, &type);
close(fd);
}
查询扩展控制
命令(请求代码):
VIDIOC_QUERY_EXT_CTRL命令参数:
struct v4l2_query_ext_ctrl
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
id |
__u32 |
控制ID |
type |
__u32 |
控制类型 |
name |
__u8[32] |
控制名称 |
minimum |
__s64 |
最小值 |
maximum |
__s64 |
最大值 |
step |
__u64 |
步长 |
default_value |
__s64 |
默认值 |
elem_size |
__u32 |
元素大小 |
elems |
__u32 |
元素数量 |
nr_of_dims |
__u32 |
维度数量 |
dims |
__u32[4] |
维度 |
用法示例:
void example_query_ext_ctrl(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_query_ext_ctrl qctrl = {0};
qctrl.id = V4L2_CID_BRIGHTNESS;
ioctl(fd, VIDIOC_QUERY_EXT_CTRL, &qctrl);
printf("Control: %s, type: %u, minimum: %lld, maximum: %lld, step: %lld, default_value: %lld\n", qctrl.name, qctrl.type, qctrl.minimum, qctrl.maximum, qctrl.step, qctrl.default_value);
close(fd);
}
备注
可用 qctrl.id = V4L2_CID_BRIGHTNESS | V4L2_CTRL_FLAG_NEXT_CTRL 从指定 ID 起迭代枚举后续控制项。
设置/获取扩展控制
命令(请求代码):
VIDIOC_S_EXT_CTRLS/VIDIOC_G_EXT_CTRLS命令参数:
struct v4l2_ext_controls
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
ctrl_class/which |
union |
控制类/控制类型 |
count |
__u32 |
控制数量 |
controls |
struct v4l2_ext_control * |
控制数组 |
controls(struct v4l2_ext_control) 字段说明:
字段 |
类型 |
用途 |
|---|---|---|
id |
__u32 |
控制ID |
size |
__u32 |
元素大小 |
value/value64/… |
union |
值/64 位值/… bit |
用法示例:
void example_set_get_ext_ctrl(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_ext_controls ctrls = {0};
struct v4l2_ext_control control = {0};
ctrls.ctrl_class = V4L2_CTRL_CLASS_USER;
ctrls.count = 1;
ctrls.controls = &control;
control.id = V4L2_CID_BRIGHTNESS;
control.value = 128;
control.size = sizeof(int);
ioctl(fd, VIDIOC_S_EXT_CTRLS, &ctrls);
memset(&ctrls, 0, sizeof(struct v4l2_ext_controls));
memset(&control, 0, sizeof(struct v4l2_ext_control));
ctrls.ctrl_class = V4L2_CTRL_CLASS_USER;
ctrls.count = 1;
ctrls.controls = &control;
control.id = V4L2_CID_BRIGHTNESS;
control.size = sizeof(int);
ioctl(fd, VIDIOC_G_EXT_CTRLS, &ctrls);
printf("Brightness: %d\n", control.value);
close(fd);
}
VIDIOC_S_EXT_CTRLS 可以一次设置单个或多个控制项。下面以相机传感器曝光时间为例:
static void config_exposure_time(int cam_fd, int32_t exposure)
{
struct v4l2_query_ext_ctrl qctrl;
struct v4l2_ext_controls controls;
struct v4l2_ext_control control[1];
qctrl.id = V4L2_CID_EXPOSURE_ABSOLUTE;
/* 查询该控制项的取值范围 */
ioctl(cam_fd, VIDIOC_QUERY_EXT_CTRL, &qctrl);
ESP_LOGW(TAG, "EXP min: %d, EXP max: %d, step:%d", (int)qctrl.minimum, (int)qctrl.maximum, (int)qctrl.step);
controls.ctrl_class = V4L2_CID_CAMERA_CLASS;
controls.count = 1;
controls.controls = control;
control[0].id = V4L2_CID_EXPOSURE_ABSOLUTE;
control[0].value = exposure / 100;
if (ioctl(cam_fd, VIDIOC_S_EXT_CTRLS, &controls) != 0) {
ESP_LOGE(TAG, "failed to set exposure time");
} else {
ESP_LOGW(TAG, "set exposure");
}
}
备注
通过 V4L2_CTRL_CLASS_ESP_CAM_IOCTL 可直接调用 esp_cam_sensor 的 ioctl(仅支持 v4l2_ext_control 的 p_u8 与 size 字段)。H.264 编码器支持在开流后设置 I 帧间隔、码率、QP 等控制项。
查询菜单
命令(请求代码):
VIDIOC_QUERYMENU命令参数:
struct v4l2_querymenu
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
id |
__u32 |
菜单ID |
index |
__u32 |
菜单索引 |
name/value |
union |
菜单名称/菜单值 |
用法示例:
void example_query_menu(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_querymenu qmenu = {0};
qmenu.id = V4L2_CID_GAIN;
qmenu.index = 0;
ioctl(fd, VIDIOC_QUERYMENU, &qmenu);
printf("Gain: value: %lld\n", qmenu.value);
close(fd);
}
设置/获取选择区域
命令(请求代码):
VIDIOC_S_SELECTION/VIDIOC_G_SELECTION命令参数:
struct v4l2_selection适用范围:MIPI-CSI 视频设备的图像裁剪
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
type |
__u32 |
缓冲区类型 |
target |
__u32 |
选择区域目标 |
r |
struct v4l2_rect |
选择区域 |
r(struct v4l2_rect) 字段说明:
字段 |
类型 |
用途 |
|---|---|---|
left |
__s32 |
左边界 |
top |
__s32 |
上边界 |
width |
__u32 |
宽度 |
height |
__u32 |
高度 |
用法示例:
void example_set_get_selection(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_selection selection = {0};
selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
selection.target = V4L2_SEL_TGT_CROP;
selection.r.left = 0;
selection.r.top = 0;
selection.r.width = 640;
selection.r.height = 480;
ioctl(fd, VIDIOC_S_SELECTION, &selection);
memset(&selection, 0, sizeof(struct v4l2_selection));
selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
selection.target = V4L2_SEL_TGT_CROP;
ioctl(fd, VIDIOC_G_SELECTION, &selection);
printf("Selection: left: %d, top: %d, width: %d, height: %d\n", selection.r.left, selection.r.top, selection.r.width, selection.r.height);
close(fd);
}
设置/获取流参数
命令(请求代码):
VIDIOC_S_PARM/VIDIOC_G_PARM命令参数:
struct v4l2_streamparm适用范围:采集设备的帧间隔;H.264 编码器可用
timeperframe配置/查询 FPS,且支持在开流后修改编码参数
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
type |
__u32 |
流类型 |
v4l2_captureparm/v4l2_outputparm |
union |
采集参数 |
capture(struct v4l2_captureparm) 字段说明:
字段 |
类型 |
用途 |
|---|---|---|
capability |
__u32 |
捕获能力 |
timeperframe |
struct v4l2_fract |
每帧时间间隔 |
用法示例:
void example_set_get_parm(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_streamparm sparm = {0};
sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
sparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
sparm.parm.capture.timeperframe.numerator = 1;
sparm.parm.capture.timeperframe.denominator = 30;
ioctl(fd, VIDIOC_S_PARM, &sparm);
memset(&sparm, 0, sizeof(struct v4l2_streamparm));
sparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_G_PARM, &sparm);
printf("Time per frame: %d/%d\n", sparm.parm.capture.timeperframe.numerator, sparm.parm.capture.timeperframe.denominator);
close(fd);
}
扩展命令
以下为 esp_video 在标准 V4L2 之外提供的请求代码,用于超时、传感器/电机格式、引用计数、MIPI-CSI 事件和设备重启。
设置/获取缓冲区出队列超时时间
命令(请求代码):
VIDIOC_S_DQBUF_TIMEOUT/VIDIOC_G_DQBUF_TIMEOUT命令参数:
struct timeval
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
tv_sec |
__s32 |
秒 |
tv_usec |
__s32 |
微秒 |
用法示例:
void example_set_get_dqbuf_timeout(void)
{
int fd = open("/dev/video0", O_RDWR);
struct timeval timeout = {0};
timeout.tv_sec = 1;
timeout.tv_usec = 0;
ioctl(fd, VIDIOC_S_DQBUF_TIMEOUT, &timeout);
memset(&timeout, 0, sizeof(struct timeval));
ioctl(fd, VIDIOC_G_DQBUF_TIMEOUT, &timeout);
printf("DQBUF timeout: %d/%d\n", timeout.tv_sec, timeout.tv_usec);
close(fd);
}
设置/获取/枚举相机传感器输出格式
命令(请求代码):
VIDIOC_S_SENSOR_FMT/VIDIOC_G_SENSOR_FMT/VIDIOC_ENUM_SENSOR_FMTVIDIOC_S_SENSOR_FMT/VIDIOC_G_SENSOR_FMT参数:esp_cam_sensor_format_tVIDIOC_ENUM_SENSOR_FMT参数:struct v4l2_sensor_format_enum
esp_cam_sensor_format_t 字段说明见头文件定义。
struct v4l2_sensor_format_enum 主要字段:
字段 |
类型 |
用途 |
|---|---|---|
index |
uint32_t |
由应用填写的格式序号,从 0 开始递增枚举 |
format |
esp_cam_sensor_format_t |
驱动返回的传感器格式;其中指针字段指向驱动静态数据,可用于随后的 |
推荐流程:先用 VIDIOC_ENUM_SENSOR_FMT 枚举并选择 sensor format,再调用标准
VIDIOC_ENUM_FMT / VIDIOC_ENUM_FRAMESIZES / VIDIOC_ENUM_FRAMEINTERVALS
查询当前 sensor 模式下设备支持的 V4L2 输出格式,最后 VIDIOC_S_FMT 并采集。
VIDIOC_S_SENSOR_FMT 须在未申请缓冲时调用。若已 VIDIOC_REQBUFS 且 count>0,该命令返回失败(errno 为 EBUSY),需先 VIDIOC_REQBUFS 且 count=0 释放缓冲后再设置。
用法示例:
void example_enum_set_sensor_format(void)
{
int fd = open("/dev/video0", O_RDWR);
struct v4l2_sensor_format_enum sensor_enum = {0};
struct v4l2_fmtdesc fmtdesc = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
struct v4l2_format format = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
};
/* 1. 枚举 sensor format */
for (sensor_enum.index = 0;
ioctl(fd, VIDIOC_ENUM_SENSOR_FMT, &sensor_enum) == 0;
sensor_enum.index++) {
printf("[%u] %s %ux%u @%ufps\n",
sensor_enum.index,
sensor_enum.format.name,
sensor_enum.format.width,
sensor_enum.format.height,
sensor_enum.format.fps);
}
/* 2. 选择并配置 sensor format(这里以 index 0 为例) */
sensor_enum.index = 0;
ioctl(fd, VIDIOC_ENUM_SENSOR_FMT, &sensor_enum);
ioctl(fd, VIDIOC_S_SENSOR_FMT, &sensor_enum.format);
/* 3. 枚举当前模式下设备支持的 V4L2 输出格式 */
uint32_t selected_pixelformat = 0;
for (fmtdesc.index = 0; ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0; fmtdesc.index++) {
printf("V4L2 format[%u]: " V4L2_FMT_STR "\n",
fmtdesc.index, V4L2_FMT_STR_ARG(fmtdesc.pixelformat));
if (fmtdesc.index == 0) {
selected_pixelformat = fmtdesc.pixelformat;
}
}
/* 4. 配置 V4L2 format 后即可 REQBUFS / STREAMON 抓图 */
ioctl(fd, VIDIOC_G_FMT, &format);
format.fmt.pix.pixelformat = selected_pixelformat;
ioctl(fd, VIDIOC_S_FMT, &format);
close(fd);
}
设置/获取自动对焦电机格式
命令(请求代码):
VIDIOC_S_MOTOR_FMT/VIDIOC_G_MOTOR_FMT命令参数:
esp_cam_motor_format_t适用范围:已使能相机电机控制器(
CONFIG_ESP_VIDEO_ENABLE_CAMERA_MOTOR_CONTROLLER)
主要字段:
字段 |
类型 |
用途 |
|---|---|---|
name |
const char * |
格式名称 |
mode |
esp_cam_motor_mode_t |
控制模式(DIRECT / LSC / DLC) |
step_period |
struct |
步进周期与每步 code 数 |
init_position |
int |
该格式对应的初始位置 |
用法示例:
void example_set_get_motor_format(void)
{
int fd = open("/dev/video0", O_RDWR);
esp_cam_motor_format_t format = {0};
ioctl(fd, VIDIOC_G_MOTOR_FMT, &format);
printf("Motor format: %s\n", format.name);
ioctl(fd, VIDIOC_S_MOTOR_FMT, &format);
close(fd);
}
设置设备引用计数
命令(请求代码):
VIDIOC_SET_OWNER命令参数:
int *
功能说明:入参非 0 时增加设备引用计数,为 0 时减少引用计数。多打开/关闭同一视频设备时用于保持设备存活。
用法示例:
void example_set_owner(void)
{
int fd = open("/dev/video0", O_RDWR);
int owner = 1;
ioctl(fd, VIDIOC_SET_OWNER, &owner);
owner = 0;
ioctl(fd, VIDIOC_SET_OWNER, &owner);
close(fd);
}
订阅/取消订阅事件
命令(请求代码):
VIDIOC_SUBSCRIBE_EVENT/VIDIOC_UNSUBSCRIBE_EVENT命令参数:
struct v4l2_event_subscription适用范围:仅 MIPI-CSI;依赖
ESP_VIDEO_CSI_DRIVER_HAS_EVENT。ESP-IDF 若未提供 MIPI-CSI 错误事件,则本命令不可用。
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
type |
__u32 |
事件类型。应用可订阅 |
id |
__u32 |
事件 ID(当前未使用) |
flags |
__u32 |
订阅标志 |
当前支持的事件类型:
类型 |
说明 |
|---|---|
|
MIPI-CSI 主机错误。载荷为 |
|
取消订阅后由驱动投递给 |
|
内部使用。事件回调返回 true 时禁用硬件中断并投递本事件,应用不应订阅 |
host_err_mask 位定义:
ESP_MIPI_CSI_HOST_ERR_PHY:PHY 错误ESP_MIPI_CSI_HOST_ERR_PACKET:包错误ESP_MIPI_CSI_HOST_ERR_FRAME:帧边界或序号错误ESP_MIPI_CSI_HOST_ERR_CRC:CRC 错误ESP_MIPI_CSI_HOST_ERR_DATA_ID:无法识别的 data type
用法示例:
void example_subscribe_event(int fd)
{
struct v4l2_event_subscription sub = {
.type = V4L2_EVENT_ESP_MIPI_CSI_ERROR,
};
ioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
/* 取消全部订阅 */
sub.type = V4L2_EVENT_ALL;
ioctl(fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub);
}
取出事件
命令(请求代码):
VIDIOC_DQEVENT命令参数:
struct v4l2_event适用范围:同
VIDIOC_SUBSCRIBE_EVENT。通常在独立任务中循环调用。
主要字段:
字段 |
类型 |
用途 |
|---|---|---|
type |
__u32 |
事件类型 |
timestamp |
struct timespec |
时间戳 |
sequence |
__u32 |
序号 |
u.data |
__u8[] |
事件载荷。CSI 错误时按 |
用法示例:
void example_dqevent(int fd)
{
struct v4l2_event event;
if (ioctl(fd, VIDIOC_DQEVENT, &event) < 0) {
return;
}
if (event.type == V4L2_EVENT_ESP_MIPI_CSI_ERROR) {
v4l2_event_esp_mipi_csi_error_t *err =
(v4l2_event_esp_mipi_csi_error_t *)&event.u.data;
printf("CSI error mask: 0x%x\n", (unsigned)err->host_err_mask);
}
}
完整事件跟踪示例见 esp_video/examples/common_components/example_video_common/example_video_event.c。
设置事件回调
命令(请求代码):
VIDIOC_S_EVENT_CALLBACK命令参数:
struct v4l2_event_callback适用范围:同 MIPI-CSI 事件命令
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
callback_func |
|
中断上下文回调。返回 true 时禁用硬件中断并投递 |
user_data |
void * |
回调用户数据 |
注意
回调必须放在 IRAM 中,且不得调用可能阻塞的函数(如 malloc / free / printf),否则可能导致系统崩溃。
用法示例:
static bool IRAM_ATTR example_csi_isr(void *user_data, uint32_t err_mask)
{
(void)user_data;
(void)err_mask;
return false; /* 不禁用硬件中断 */
}
void example_set_event_callback(int fd)
{
struct v4l2_event_callback cb = {
.callback_func = example_csi_isr,
.user_data = NULL,
};
ioctl(fd, VIDIOC_S_EVENT_CALLBACK, &cb);
}
重启视频设备
命令(请求代码):
VIDIOC_RESTART命令参数:
struct v4l2_restart_config适用范围:必须在开流之后、停流之前调用,用于硬件异常后恢复。
字段说明:
字段 |
类型 |
用途 |
|---|---|---|
type |
int |
|
restart_sensor |
bool |
是否同时重启相机传感器 |
用法示例:
void example_restart(int fd)
{
struct v4l2_restart_config config = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.restart_sensor = true,
};
ioctl(fd, VIDIOC_RESTART, &config);
}
控制 ID 一览
以下为当前实现的主要控制 ID,配合前文 VIDIOC_QUERY_EXT_CTRL / VIDIOC_S_EXT_CTRLS 使用。具体设备是否支持某项,请用 VIDIOC_QUERY_EXT_CTRL 查询。
ID |
Class |
说明 |
|---|---|---|
|
USER |
垂直/水平镜像 |
|
USER |
像素增益 |
|
USER |
曝光,单位取决于传感器 |
|
CAMERA |
曝光,单位 100 us |
|
IMAGE_PROC |
测试图案 |
|
JPEG |
JPEG 压缩质量 |
|
JPEG |
JPEG 色度抽样 |
|
CODEC |
H.264 I 帧间隔 |
|
CODEC |
H.264 码率(bit/s) |
|
CODEC |
H.264 量化参数范围 |
|
USER |
红/蓝色度增益 |
|
USER |
ISP 亮度/对比度/饱和度/色相 |
|
USER |
ISP Bayer 滤波 |
|
USER |
ISP 色彩校正矩阵 |
|
USER |
ISP 锐化 |
|
USER |
ISP GAMMA;EXT 可分通道配置 |
|
USER |
ISP 去马赛克 |
|
USER |
ISP AWB / AE / AF / Histogram 窗口与统计 |
|
USER |
ISP 镜头阴影校正 |
|
USER |
ISP 黑电平校正 |
|
USER |
输入输出均为 RAW8 时旁路 ISP |
|
CAMERA |
传感器统计 |
|
CAMERA |
传感器 AE 目标 |
|
CAMERA |
曝光与增益组参数 |
|
CAMERA |
对焦电机启动时间 |