mplane方式v4l2应用分析 -- VIDIOC_S_FMT(设置图像格式)

Linux v4l2架构学习总链接

gitee源码

设置图像格式 VIDIOC_S_FMT

v4l2应用代码如下:

	struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    fmt.fmt.pix_mp.width = 2400;
    fmt.fmt.pix_mp.height = 1920;
    fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_SRGGB12;
    fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
        printf("Set format fail\n");
        goto err;
    }   

    printf("width = %d\n", fmt.fmt.pix_mp.width);
    printf("height = %d\n", fmt.fmt.pix_mp.height);

驱动调用顺序如下:

v4l_s_fmt ->
	vidioc_s_fmt_vid_cap_mplane ->
	rkcif_s_fmt_vid_cap_mplane ->
		rkcif_set_fmt

分析最终源码:

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如下		
	 * @cplanes: number of colour planes
   * @mplanes: number of planes for format
	 * .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;

	/* 注意:
	 * 虽然我们传入了width和height,但是还是需要从sensor获取真实的width和height
	 * 防止超过实际范围
	 */
	if (dev->active_sensor && dev->active_sensor->sd)
		get_input_fmt(dev->active_sensor->sd,
			      &input_rect, stream->id + 1);

	/* 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(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;
		
		... ... ...
		
		size = bpl * height;
		imagesize += size;

		if (fmt->mplanes > i) {
			/* Set bpl and size for each mplane */
			plane_fmt = pixm->plane_fmt + i;
			plane_fmt->bytesperline = bpl;
			plane_fmt->sizeimage = size;
		}
		v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev,
			 "C-Plane %i size: %d, Total imagesize: %d\n",
			 i, size, imagesize);
	}

	/* convert to non-MPLANE format.
	 * It's important since we want to unify non-MPLANE
	 * and MPLANE.
	 */
	/* rk的注释,意思是统一格式为non-MPLANE
	 */
	if (fmt->mplanes == 1)
		pixm->plane_fmt[0].sizeimage = imagesize;

	/* try 为 false
	 * stream中保存相关数据
	 */
	if (!try) {
		stream->cif_fmt_out = fmt;
		stream->pixm = *pixm;

		v4l2_dbg(1, rkcif_debug, &stream->cifdev->v4l2_dev,
			 "%s: req(%d, %d) out(%d, %d)\n", __func__,
			 pixm->width, pixm->height,
			 stream->pixm.width, stream->pixm.height);
	}
}

上面的代码删除了计算imagesize部分的代码,这里不影响分析。
以上面对代码为例,传入的是2400x1920,但是实际sensor是1280x1024,那么最终打印的也是1280x1024,也就是说S_FMT的ioctl会根据实际sensor进行修改。

针对V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE类型,VIDIOC_REQBUFS和VIDIOC_QUERYBUF需要按照以下步骤进行: 1. VIDIOC_REQBUFS:首先,使用VIDIOC_REQBUFS命令来请求缓冲区,需要设置struct v4l2_requestbuffers结构体中的成员,如下所示: ``` struct v4l2_requestbuffers reqbuf; reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; reqbuf.memory = V4L2_MEMORY_MMAP; reqbuf.count = 4; ``` 其中,type成员表明请求的是视频捕获缓冲区,memory成员表明使用内存映射方式,count成员表明请求4个缓冲区。 2. VIDIOC_QUERYBUF:请求缓冲区之后,需要使用VIDIOC_QUERYBUF命令来查询每个缓冲区的信息,需要设置struct v4l2_buffer和struct v4l2_plane结构体中的成员,如下所示: ``` struct v4l2_buffer buf; struct v4l2_plane planes[VIDEO_MAX_PLANES]; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; buf.length = VIDEO_MAX_PLANES; buf.m.planes = planes; for (int i = 0; i < buf.length; i++) { buf.m.planes[i].length = buffer_size; buf.m.planes[i].m.mem_offset = i * buffer_size; buf.m.planes[i].bytesused = 0; } ``` 其中,type成员和memory成员同样表明请求的是视频捕获缓冲区,index成员表明查询第一个缓冲区的信息,length成员表明缓冲区中的平面数,m.planes成员表示缓冲区中的每个平面的信息,包括长度、内存偏移和已使用的字节数。需要注意的是,buffer_size是每个平面的大小,需要根据实际情况设置
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dianlong_lee

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

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

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

打赏作者

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

抵扣说明:

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

余额充值