一. 前言
本文分享Linux下QV4L2无法选择UVC帧格式的问题,问题本身不复杂,主要分享调试分析方法和过程。 问题是在Linux下使用QV4L2无法选择对应的帧格式:选择不同的格式,总是变为默认的格式(这里默认格式是MJPG)
二. 前置知识
基于WSL修改驱动增加打印信息,替换内核进行调试。
见WSL中支持UVC参考公众号文章
WSL2中配置支持UVC
三. 分析过程
3.1 尝试查看UVC驱动层LOG
先使能UVC的dbg信息(必须切换到su再修改,直接sudo修改会提示权限问题)
sudo su
echo 0x01FFF > /sys/module/uvcvideo/parameters/trace
查看设置值
cat /sys/module/uvcvideo/parameters/trace
然后清除dmesg
sudo dmesg -c
确认清除
dmesg
然后运行qv4l2后,dmesg查看log
看到打印信息如下
root@qinyunti:/home/qinyunti# dmesg[ 1361.655464] usb 1-1: uvc_v4l2_open[ 1361.724433] usb 1-1: Resuming interface 0[ 1361.724442] usb 1-1: Resuming interface 1[ 1361.724446] usb 1-1: Resuming interface 2[ 1361.724449] usb 1-1: Resuming interface 3[ 1361.876293] usb 1-1: uvc_v4l2_release[ 1361.876383] usb 1-1: uvc_v4l2_open[ 1361.877772] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.877778] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.882461] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.882470] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.886490] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.886501] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.890596] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.890605] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.894375] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.894384] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.898814] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.898824] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.902613] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.902623] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.906474] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.906482] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.910249] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1361.910257] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1361.916444] usb 1-1: Control 0x00980900 not found[ 1361.923695] usb 1-1: uvc_v4l2_poll
选择NV12格式,查看打印信息如下
[ 1462.642038] usb 1-1: uvc_v4l2_poll[ 1462.643073] usb 1-1: Trying format 0x3231564e (NV12): 1280x720[ 1462.643081] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.648836] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.648848] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.654511] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.654522] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.660700] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.660709] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.666833] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.666842] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.672974] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.672984] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.677502] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.677513] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.681863] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.681872] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.687086] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.687162] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.691518] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.691526] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.695737] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.695745] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.699750] usb 1-1: Trying format 0x47504a4d (MJPG): 1280x720[ 1462.699758] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)[ 1462.705050] usb 1-1: uvc_v4l2_poll[ 1462.707470] usb 1-1: uvc_v4l2_poll[ 1462.707485] usb 1-1: uvc_v4l2_poll[ 1462.889325] usb 1-1: uvc_v4l2_poll
可以看到先是开始try NV12格式的但是最后又选择了默认的mjpeg
3.2 分析代码
那么我们根据前面的打印信息找到
[ 1462.643073] usb 1-1: Trying format 0x3231564e (NV12): 1280x720
的位置
搜索Trying format
位于
drivers/media/usb/uvc/uvc_v4l2.c的
staticintuvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, const struct uvc_format **uvc_format, const struct uvc_frame **uvc_frame){ const struct uvc_format *format = NULL; const struct uvc_frame *frame = NULL; u16 rw, rh; unsigned int d, maxd; unsigned int i; u32 interval; int ret = 0; u8 *fcc; if (fmt->type != stream->type) return -EINVAL; fcc = (u8 *)&fmt->fmt.pix.pixelformat; uvc_dbg(stream->dev, FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u\n", fmt->fmt.pix.pixelformat, fcc[0], fcc[1], fcc[2], fcc[3], fmt->fmt.pix.width, fmt->fmt.pix.height);
3.3 定位大概位置
猜测这个函数选择NV12时返回失败了,那么我们手动加一些打印信息
我们看到打印
[ 1462.643073] usb 1-1: Trying format 0x3231564e (NV12): 1280x720
[ 1462.643081] usb 1-1: Using default frame interval 40000.0 us (25.0 fps)
所以至少跑到了如下位置以后
所以在后面加打印
但是如下位置后面没有打印,所以猜测是不是ret = uvc_probe_video(stream, probe);
返回了失败
增加打印确认
/* Probe the device. */ ret = uvc_probe_video(stream, probe); mutex_unlock(&stream->mutex); if (ret < 0){ uvc_dbg(stream->dev, FORMAT,"uvc_probe_video err ret %d\r\n", ret); return ret; }
查看实际这里也没有打印,说明上述猜测错误,即
uvc_v4l2_try_format是成功的
没有报错。
3.4 增加打印确认状态
上述地方没有报错,那么就继续增加一些打印信息,打印一些状态
首先看代码,这里有说明
没有probe到需要的格式,也不会返回错误,而是最终设备响应啥格式就是啥格式
/* * After the probe, update fmt with the values returned from * negotiation with the device. Some devices return invalid bFormatIndex * and bFrameIndex values, in which case we can only assume they have * accepted the requested format as-is. */ for (i = 0; i < stream->nformats; ++i) { if (probe->bFormatIndex == stream->formats[i].index) { format = &stream->formats[i]; break; } }
所以我们就来看这里实际选择的是什么格式
在uvc_v4l2_try_format中如下位置最后增加打印
uvc_dbg(stream->dev, FORMAT,"format %d frame %d\r\n", probe->bFormatIndex,probe->bFrameIndex);
看到选择不同格式都是打印信息如下,即设备返回的格式总是不变的。
即设备对probe的响应错误。
四. 原因
既然上述确认是设备返回不对,就看设备的代码
原来设备端代码中对于SET处理笔误写错了,SET信息没有更新,所以一直返回默认格式,所以这里qv4l2获取的格式就一直是固定的,无法修改。
SET时,SET的信息存储到了s_probe_cur_tmp
应该是要存到s_probe_cur的
因为GET时是从s_probe_cur获取值返回给HOST的
所以代码中SET CUR之后没有更新记录,之后GET CUR的格式索引不对,返回不是SET的值而一直是默认值。
修改之后,此时就可以看到按照指定更新了
这里顺便提一下,windows下上述设备返回固定值时,potplayer中GUI显示的还是选择的指定格式的,哪怕设备返回的是固定的格式,所以windows的处理还是有一点差异,windows下一般来说能更多的容忍一些小错误,看起来就是兼容性会更好。
五. 总结
问题本身很简单就是一个笔误导致的问题,主要分享遇到类似问题如何去分析调试。也进一步说明基于WSL进行linux开发调试真的很方便。