Linux v4l2架构学习总链接

前天机芯部门给了我一个mipi的摄像头,让我接到rv1126上看看能不能抓到图。这个感觉简单啊,而且我们的摄像头都不需要配置寄存器。

这个摄像头参数:raw8,2lanes,512*192,25fps

复制一份imx291的代码直接修改,改完之后测试

测试方法如下:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=512,height=192,pixelformat=BG12 --stream-mmap=3 --stream-to=/tmp/bg12.bin --stream-count=1 --stream-poll

执行发现,没有抓到数据也没有timeout,直接退出了。

dmesg发现出现了如下一条信息

rkcif_mipi_lvds: crop size is bigger than input

这部分代码如下:

rkcif_start_streaming() -> rkcif_sanity_check_fmt(stream, NULL)

static int rkcif_sanity_check_fmt(struct rkcif_stream *stream,
				  const struct v4l2_rect *s_crop)
{
	struct rkcif_device *dev = stream->cifdev;
	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
	struct v4l2_rect input, *crop;

	stream->cif_fmt_in = get_input_fmt(dev->active_sensor->sd,
					   &input, stream->id + 1);
	if (!stream->cif_fmt_in) {
		v4l2_err(v4l2_dev, "Input fmt is invalid\n");
		return -EINVAL;
	}

	if (s_crop)
		crop = (struct v4l2_rect *)s_crop;
	else
		crop = &stream->crop[CROP_SRC_ACT];

	if (crop->width + crop->left > input.width ||
	    crop->height + crop->top > input.height) {
		v4l2_err(v4l2_dev, "crop size is bigger than input\n");
		return -EINVAL;
	}
       
        ...
}

看代码可以知道input.width及heigth小于crop的width或者heigth

于是去找crop的赋值代码

rkcif_stream_init()

#define RKCIF_DEFAULT_WIDTH	640
#define RKCIF_DEFAULT_HEIGHT	480

void rkcif_stream_init(struct rkcif_device *dev, u32 id)
{
	...

	for (i = 0; i < CROP_SRC_MAX; i++) {
		stream->crop[i].left = 0;
		stream->crop[i].top = 0;
		stream->crop[i].width = RKCIF_DEFAULT_WIDTH;
		stream->crop[i].height = RKCIF_DEFAULT_HEIGHT;
	}

	...
}

于是一顿修改

#define RKCIF_DEFAULT_WIDTH    480
#define RKCIF_DEFAULT_HEIGHT    120

v4l2-ctl可以正常跑了,于是这件事也就扔到脑后了....

 

时间来到今天,最近一直在学习v4l2,于是建了一个群讨论v4l2相关问题。

问题的起源是:

v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080 ...

v4l2-ctl -d /dev/video0 --set-fmt-video=width=720,height=480 ...

这2条命令设置的分辨率不同,但是抓到的图的大小相同,都是1080p的大小,于是开始分析

首先开启log,抓取v4l2-ctl过程中的ioctl的调用

echo 3 > /sys/class/video4linux/video0/dev_debug

可以看到如下log

720*480

1920*1080

可以看到sizeimage都是4147200,这就导致了抓到的数据大小都一样。

看到这里就知道下一步去看对应代码

rkcif_set_fmt() -> rkcif_sync_crop_info()

static void rkcif_sync_crop_info(struct rkcif_stream *stream)
{
	struct rkcif_device *dev = stream->cifdev;
	struct v4l2_subdev_selection input_sel;
	int ret;

	if (dev->terminal_sensor.sd) {
		input_sel.target = V4L2_SEL_TGT_CROP_BOUNDS;

		ret = v4l2_subdev_call(dev->terminal_sensor.sd,
				       pad, get_selection, NULL,
				       &input_sel);
		if (!ret) {
			stream->crop[CROP_SRC_SENSOR] = input_sel.r;
			stream->crop_enable = true;
			stream->crop_mask |= CROP_SRC_SENSOR_MASK;
			dev->terminal_sensor.selection = input_sel;
		} else {
			dev->terminal_sensor.selection.r =     dev->terminal_sensor.raw_rect;
		}
	}

        stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR];
        ...
}

讨论的问题驱动代码有这个get_selection,里面将input_sel.r.width = 1920,input_sel.r.height = 1080

这里导致了v4l2-ctl修改分辨率,但是抓图数据大小不变。

 

其实到这里就可以停下了,但是由于之前阅读代码的时候跳过了crop这些代码,所以这里打算再追一下,

于是发现

    .vidioc_s_crop = rkcif_s_crop,
    .vidioc_g_crop = rkcif_g_crop,
    .vidioc_s_selection = rkcif_s_selection,
    .vidioc_g_selection = rkcif_g_selection,

这几个ioctl中都有对crop的操作,于是就想到v4l2-ctl是不是支持直接设置?

v4l2-ctl -h

可以看到一条这样的信息

--help-selection   crop/selection options

于是

4l2-ctl --help-selection 

打印如下

Selection/Cropping options:
  --get-cropcap      query the crop capabilities [VIDIOC_CROPCAP]
  --get-crop	     query the video capture crop window [VIDIOC_G_CROP]
  --set-crop top=<x>,left=<y>,width=<w>,height=<h>
                     set the video capture crop window [VIDIOC_S_CROP]
  --get-cropcap-output
                     query crop capabilities for video output [VIDIOC_CROPCAP]
  --get-crop-output  query the video output crop window [VIDIOC_G_CROP]
  --set-crop-output top=<x>,left=<y>,width=<w>,height=<h>
                     set the video output crop window [VIDIOC_S_CROP]
  --get-cropcap-overlay
                     query crop capabilities for video overlay [VIDIOC_CROPCAP]
  --get-crop-overlay query the video overlay crop window [VIDIOC_G_CROP]
  --set-crop-overlay top=<x>,left=<y>,width=<w>,height=<h>
                     set the video overlay crop window [VIDIOC_S_CROP]
  --get-cropcap-output-overlay
                     query the crop capabilities for video output overlays
                     [VIDIOC_CROPCAP]
  --get-crop-output-overlay
                     query the video output overlay crop window [VIDIOC_G_CROP]
  --set-crop-output-overlay top=<x>,left=<y>,width=<w>,height=<h>
                     set the video output overlay crop window [VIDIOC_S_CROP]
  --get-selection target=<target>
                     query the video capture selection rectangle [VIDIOC_G_SELECTION]
                     See --set-selection command for the valid <target> values.
  --set-selection target=<target>,flags=<flags>,top=<x>,left=<y>,width=<w>,height=<h>
                     set the video capture selection rectangle [VIDIOC_S_SELECTION]
                     target=crop|crop_bounds|crop_default|compose|compose_bounds|
                            compose_default|compose_padded|native_size
                     flags=le|ge|keep-config
  --get-selection-output target=<target>
                     query the video output selection rectangle [VIDIOC_G_SELECTION]
                     See --set-selection command for the valid <target> values.
  --set-selection-output target=<target>,flags=<flags>,top=<x>,left=<y>,width=<w>,height=<h>
                     set the video output selection rectangle [VIDIOC_S_SELECTION]
                     See --set-selection command for the arguments.

ok,现在可以这样操作

[root@RV1126_RV1109:/]#v4l2-ctl -d /dev/video0 --get-crop
Crop: Left 0, Top 0, Width 1920, Height 1080

[root@RV1126_RV1109:/]#v4l2-ctl -d /dev/video0 --set-crop top=0,left=0,width=720,heigth=480

[root@RV1126_RV1109:/]#v4l2-ctl -d /dev/video0 --get-crop
Crop: Left 0, Top 0, Width 720, Height 480

用v4l2-ctl测试

v4l2-ctl -d /dev/video0 --set-fmt-video=width=720,height=480 ...

发现抓到的数据大小没有问题了。

故事结束了,后面就开始分析具体的代码,看看是如何实现的

Logo

更多推荐