设置RGB4像素格式后V4L2的VIDIOC_STREAMON返回无效参数问题
核心问题分析
你遇到的VIDIOC_STREAMON: Invalid argument错误,本质是格式设置后缓冲区参数与驱动实际要求不匹配。虽然VIDIOC_S_FMT调用成功,但V4L2驱动会根据硬件能力自动调整格式参数(比如分辨率、行跨度stride、缓冲区大小),如果后续缓冲区分配/配置还是基于旧参数,就会触发启动流失败。
另外你提到的144869129对应的fourcc,实际是小端字节序解析后的BA81,这是树莓派摄像头默认的YUYV类格式,和你要设置的RGB4无关。
具体修复步骤
1. 设置格式后必须重新读取驱动实际生效的参数
VIDIOC_S_FMT调用后,驱动会把实际能支持的参数写回fmt结构体,你必须读取这些参数,后续所有缓冲区操作都要基于这个值:
struct v4l2_format fmt; // 读取当前格式 if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) { exit(EXIT_FAILURE); } // 设置目标像素格式 fmt.fmt.pix.pixelformat = v4l2_fourcc('R', 'G', 'B', '4'); // 尝试设置格式 if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) { exit(EXIT_FAILURE); } // 关键:重新读取驱动实际应用的格式参数(驱动可能调整了分辨率、stride等) if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt)) { exit(EXIT_FAILURE); } // 打印确认实际参数 printf("实际像素格式: %c%c%c%c, 分辨率: %dx%d, 行跨度: %d\n", (fmt.fmt.pix.pixelformat >> 0) & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF, (fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF, fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.bytesperline);
2. 确保格式设置在缓冲区申请之前
样板代码中VIDIOC_REQBUFS(申请缓冲区)是在格式设置前执行的,你修改格式后,必须重新执行缓冲区申请和映射,因为新格式的缓冲区大小(fmt.fmt.pix.bytesperline * fmt.fmt.pix.height)和旧格式不同:
// 设置完格式并读取实际参数后,重新申请缓冲区 struct v4l2_requestbuffers req = {0}; req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { exit(EXIT_FAILURE); } // 重新映射缓冲区(逻辑和样板代码一致,基于新的fmt参数)
3. 确认RGB4格式支持的分辨率范围
树莓派摄像头V3的RGB4格式可能只支持特定分辨率,用以下命令查看该格式的详细支持列表:
v4l2-ctl -d /dev/video0 --list-formats-ext
找到RGB4对应的条目,确保你设置的fmt.fmt.pix.width和fmt.fmt.pix.height在该列表范围内。如果之前的默认分辨率不在RGB4支持列表中,驱动会自动调整分辨率,但如果调整后的参数和后续缓冲区配置不匹配,就会报错。
4. 重新初始化缓冲区队列
如果之前已经初始化过缓冲区队列(调用过VIDIOC_QBUF),修改格式后需要先停止流(VIDIOC_STREAMOFF),释放旧缓冲区,再重新申请新缓冲区并入队。
额外注意点
- 树莓派摄像头V3的RGB4格式是
32-bit A/XRGB 8-8-8-8,行跨度bytesperline可能因硬件对齐要求大于width*4,必须使用驱动返回的bytesperline计算缓冲区大小,不能自行硬编码。 - 如果以上步骤仍未解决问题,可以先设置RGB4支持的最小分辨率,验证流是否能启动,再逐步调整到目标分辨率。
内容的提问来源于stack exchange,提问作者Jomy

