v4l2架构专题模块crop及selection分析 --- 基于rv1126平台分析

Linux v4l2架构学习总链接

相关代码位置:

https://gitee.com/ldl17/v4l2-learn/tree/master/rv1126-imx291

首先看crop的初始化代码

rkcif_plat_probe() -> rkcif_plat_init() -> rkcif_stream_init()

#define RKCIF_DEFAULT_WIDTH	640
#define RKCIF_DEFAULT_HEIGHT	480

void rkcif_stream_init(struct rkcif_device *dev, u32 id)
{
	struct rkcif_stream *stream = &dev->stream[id];
	struct v4l2_pix_format_mplane pixm;
	int i;

	memset(stream, 0, sizeof(*stream));
	
    ...

	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;
	}

	stream->crop_enable = false;
	stream->crop_mask = 0x0;
    ...
}

可以看到初始化的时候被设置成640*480

下面ioctl操作是如何会修改stream->crop的值

设置图像格式(VIDIOC_S_FMT)

以命令v4l2-ctl -d /dev/video0 --set-fmt-video=width=512,height=192,pixelformat=BG12 ... 为例


#define RKCIF_DEFAULT_WIDTH	640
#define RKCIF_DEFAULT_HEIGHT	480

static void rkcif_set_fmt(struct rkcif_stream *stream,
			  struct v4l2_pix_format_mplane *pixm,
			  bool try)
{
	struct rkcif_device *dev = stream->cifdev;
	const struct cif_output_fmt *fmt;
	struct v4l2_rect input_rect;
	unsigned int imagesize = 0, planes;
	u32 xsubs = 1, ysubs = 1, i;
	struct rkmodule_hdr_cfg hdr_cfg;
	int ret;


        /*
         * 这里根据BG12
         * 找到对应的fmt信息如下:
         * 	.fourcc = V4L2_PIX_FMT_SBGGR12,
	 *      .cplanes = 1,
	 *      .mplanes = 1,
	 *      .bpp = { 16 },
	 *      .raw_bpp = 12,
	 *      .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
	 *      .fmt_type = CIF_FMT_TYPE_RAW,
         */

	fmt = find_output_fmt(stream, pixm->pixelformat);
	if (!fmt)
		fmt = &out_fmts[0];

	input_rect.width = RKCIF_DEFAULT_WIDTH;
	input_rect.height = RKCIF_DEFAULT_HEIGHT;

	if (dev->active_sensor && dev->active_sensor->sd)
                
                /*
                 * 这里的一系列调用就不分析了,之前都已经分析过了
                 * 对应故事里的摄像头
                 * input_rect.top = 0
                 * input_rect.left = 0
                 * input_rect.width = 512
                 * input_rect.height = 192
                 */
            
		get_input_fmt(dev->active_sensor->sd,
			      &input_rect, stream->id + 1);

	if (dev->terminal_sensor.sd) {
		ret = v4l2_subdev_call(dev->terminal_sensor.sd,
				       core, ioctl,
				       RKMODULE_GET_HDR_CFG,
				       &hdr_cfg);
		if (!ret)
			dev->hdr.mode = hdr_cfg.hdr_mode;
		else
			dev->hdr.mode = NO_HDR;

                /* terminal_sensor.raw_rect 赋值*/
                
		dev->terminal_sensor.raw_rect = input_rect;
	}

	/* CIF has not scale function,
	 * the size should not be larger than input
	 */
	pixm->width = clamp_t(u32, pixm->width,
			      CIF_MIN_WIDTH, input_rect.width);
	pixm->height = clamp_t(u32, pixm->height,
			       CIF_MIN_HEIGHT, input_rect.height);
	pixm->num_planes = fmt->mplanes;
	pixm->field = V4L2_FIELD_NONE;
	pixm->quantization = V4L2_QUANTIZATION_DEFAULT;

        
        /*
         * rkcif_sync_crop_info 看下面的分析
         */

	rkcif_sync_crop_info(stream);
	/* calculate plane size and image size */
	fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);

	planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;

	for (i = 0; i < planes; i++) {
		struct v4l2_plane_pix_format *plane_fmt;
		int width, height, bpl, size, bpp;

		if (i == 0) {

                        /*
                         * 根据上面分析,由于没有get_selection
                         * 所以stream->crop_enable值为false
                         */
			if (stream->crop_enable) {
				width = stream->crop[CROP_SRC_ACT].width;
				height = stream->crop[CROP_SRC_ACT].height;
			} else {
				width = pixm->width;
				height = pixm->height;
			}
		} else {
			if (stream->crop_enable) {
				width = stream->crop[CROP_SRC_ACT].width / xsubs;
				height = stream->crop[CROP_SRC_ACT].height / ysubs;
			} else {
				width = pixm->width / xsubs;
				height = pixm->height / ysubs;
			}
		}

		/* compact mode need bytesperline 4bytes align,
		 * align 8 to bring into correspondence with virtual width.
		 * to optimize reading and writing of ddr, aliged with 256.
		 */
		...
	}

	...
}

 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;

                /*
                 * 这里会调用sensor驱动的 pad->get_selection
                 * 我手里的驱动这个是没有实现的,所以会执行下面的else
                 */

		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;
		}
	}

        /*
         * CROP_SRC_SENSOR_MASK
         *    这个mask设置是在get_slection正常返回后
         *    因为sensor没有 get_slection,所以没有CROP_SRC_SENSOR_MASK
         * CROP_SRC_USR_MASK
         *    这个mask是ioctl VIDIOC_S_CROP后设置
         *    后面会分析到
         */

	if ((stream->crop_mask & 0x3) == (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) {
		if (stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_USR].width >
		    stream->crop[CROP_SRC_SENSOR].width ||
		    stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_USR].height >
		    stream->crop[CROP_SRC_SENSOR].height)
			stream->crop[CROP_SRC_USR] = stream->crop[CROP_SRC_SENSOR];
	}

	if (stream->crop_mask & CROP_SRC_USR_MASK) {
		stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
		if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
			stream->crop[CROP_SRC_ACT].left = stream->crop[CROP_SRC_USR].left +
							  stream->crop[CROP_SRC_SENSOR].left;
			stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top +
							  stream->crop[CROP_SRC_SENSOR].top;
		}
	} else {

                /*
                 * 可以知道执行这个分支代码
                 * 由于没有get_selection
                 * 所以stream->crop[CROP_SRC_SENSOR]没有被更新
                 * 所以还是只是初始化的值
                 */

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

 看一下报错的代码

static int rkcif_sanity_check_fmt(struct rkcif_stream *stream,
				  const struct v4l2_rect *s_crop)
{
    ...
    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;
    }
    ...
}

由上面的分析可以知道

crop.width = 640

crop.height = 480

input.width = 512

input.height = 192

裁剪的大小大于输入的大小,所以报错

下面解决这个错误

v4l2-ctl -d /dev/video0 --set-crop top=0,left=0,width=480,height=120

对应VIDIOC_S_CROP

驱动代码如下

static int rkcif_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
{
	struct rkcif_stream *stream = video_drvdata(file);
	struct rkcif_device *dev = stream->cifdev;
	const struct v4l2_rect *rect = &a->c;
	struct v4l2_rect sensor_crop;
	int ret;

	v4l2_info(&dev->v4l2_dev, "S_CROP(%ux%u@%u:%u) type: %d\n",
		  rect->width, rect->height, rect->left, rect->top, a->type);

        
        /*
         * 这里不跟进分析
         * 可以认为没有问题
         */    
    

	ret = rkcif_sanity_check_fmt(stream, rect);
	if (ret)
		return ret;


        /*
         * sensor没有get_selection
         * 所以没有设置CROP_SRC_SENSOR_MASK
         */
	if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
		sensor_crop = stream->crop[CROP_SRC_SENSOR];
		if (sensor_crop.left + rect->left  + rect->width > sensor_crop.width ||
		    sensor_crop.top + rect->top + rect->height > sensor_crop.height) {
			v4l2_err(&dev->v4l2_dev, "crop size is bigger than sensor input:left:%d, top:%d, width:%d, height:%d\n",
				 sensor_crop.left, sensor_crop.top, sensor_crop.width, sensor_crop.height);
			return -EINVAL;
		}
	}

        /*
         * 更新stream->crop[CROP_SRC_USR]
         * 并且设置CROP_SRC_USR_MASK
         */

	stream->crop[CROP_SRC_USR] = *rect;
	stream->crop_enable = true;
	stream->crop_mask |= CROP_SRC_USR_MASK;
	stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
	if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
		stream->crop[CROP_SRC_ACT].left = sensor_crop.left + stream->crop[CROP_SRC_USR].left;
		stream->crop[CROP_SRC_ACT].top =  sensor_crop.top + stream->crop[CROP_SRC_USR].top;
	}

	return ret;
}

设置这个后,抓图为什么就ok了?

这就要再分析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;

                /*
                 * 这里会调用sensor驱动的 pad->get_selection
                 * 我手里的驱动这个是没有实现的,所以会执行下面的else
                 */

		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;
		}
	}

        /*
         * CROP_SRC_SENSOR_MASK
         *    这个mask设置是在get_slection正常返回后
         *    因为sensor没有 get_slection,所以没有CROP_SRC_SENSOR_MASK
         * 执行了 ioctl VIDIOC_S_CROP 后
         * CROP_SRC_USR_MASK 被设置了
         */

	if ((stream->crop_mask & 0x3) == (CROP_SRC_USR_MASK | CROP_SRC_SENSOR_MASK)) {
		if (stream->crop[CROP_SRC_USR].left + stream->crop[CROP_SRC_USR].width >
		    stream->crop[CROP_SRC_SENSOR].width ||
		    stream->crop[CROP_SRC_USR].top + stream->crop[CROP_SRC_USR].height >
		    stream->crop[CROP_SRC_SENSOR].height)
			stream->crop[CROP_SRC_USR] = stream->crop[CROP_SRC_SENSOR];
	}

        /* CROP_SRC_USR_MASK 被设置 */

	if (stream->crop_mask & CROP_SRC_USR_MASK) {

                /*
                 * crop[CROP_SRC_ACT]值被更新
                 * crop区域大小在input范围内,所以不会出现之前的报错
                 */

		stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_USR];
		if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
			stream->crop[CROP_SRC_ACT].left = stream->crop[CROP_SRC_USR].left +
							  stream->crop[CROP_SRC_SENSOR].left;
			stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top +
							  stream->crop[CROP_SRC_SENSOR].top;
		}
	} else {

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

再看看VIDIOC_S_SELECTION

对应驱动

static int rkcif_s_selection(struct file *file, void *fh,
				  struct v4l2_selection *s)
{
	struct rkcif_stream *stream = video_drvdata(file);
	struct rkcif_device *dev = stream->cifdev;
	struct v4l2_subdev *sensor_sd;
	struct v4l2_subdev_selection sd_sel;
	u16 pad = 0;
	int ret = 0;

	if (!s) {
		v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "sel is null\n");
		goto err;
	}

	sensor_sd = get_remote_sensor(stream, &pad);

	sd_sel.r = s->r;
	sd_sel.pad = pad;
	sd_sel.target = s->target;
	sd_sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;

	ret = v4l2_subdev_call(sensor_sd, pad, set_selection, NULL, &sd_sel);
	if (!ret) {
		s->r = sd_sel.r;
		v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "%s: pad:%d, which:%d, target:%d\n",
			 __func__, pad, sd_sel.which, sd_sel.target);
	}

	return ret;

err:
	return -EINVAL;
}

其实这里不用分析了,因为sensor也没有set_selection,所以这里的ioctl应该会报错

[root@RV1126_RV1109:/]# v4l2-ctl -d /dev/video0 --set-selection target=crop,flag
s=keep-config,top=0,left=0,width=480,height=120
VIDIOC_S_SELECTION: failed: Inappropriate ioctl for device

内核log如下

[ 1118.387010] video0: VIDIOC_S_SELECTION: error -515: type=vid-cap, target=0, flags=0x4, wxh=480x120, x,y=0,0

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值