From fa1bd8d2d2570837d632610df20407a64b8a6250 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Fri, 27 Sep 2019 18:52:24 +0800 Subject: [PATCH] libv4l: mplane: Filter out multiplane formats The multiplane formats are not supported here, reporting them to the userspace might cause unexpected results. Signed-off-by: Jeffy Chen --- lib/libv4l-mplane/libv4l-mplane.c | 149 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 8 deletions(-) diff --git a/lib/libv4l-mplane/libv4l-mplane.c b/lib/libv4l-mplane/libv4l-mplane.c index 2f685a73..b30840a3 100644 --- a/lib/libv4l-mplane/libv4l-mplane.c +++ b/lib/libv4l-mplane/libv4l-mplane.c @@ -49,6 +49,15 @@ #define PLUGIN_PUBLIC #endif +#define MPLANE_MAX_FORMATS 32 + +struct mplane_formats { + struct v4l2_format formats[MPLANE_MAX_FORMATS]; + int index_map[MPLANE_MAX_FORMATS]; + unsigned int num_formats; + int def_format; +}; + struct mplane_plugin { union { struct { @@ -57,6 +66,9 @@ struct mplane_plugin { }; unsigned int mplane; }; + + struct mplane_formats capture_formats; + struct mplane_formats output_formats; }; #define SIMPLE_CONVERT_IOCTL(fd, cmd, arg, __struc) ({ \ @@ -75,6 +87,55 @@ struct mplane_plugin { __ret; \ }) +/* Setup supported(single plane) formats */ +static void mplane_setup_formats(int fd, struct mplane_formats *formats, + enum v4l2_buf_type type) +{ + int ret, n; + + formats->num_formats = 0; + formats->def_format = -1; + + for (n = 0; formats->num_formats < MPLANE_MAX_FORMATS; n++) { + struct v4l2_fmtdesc fmtdesc = { 0 }; + struct v4l2_format format = { 0 }; + + fmtdesc.type = type; + fmtdesc.index = n; + + ret = SYS_IOCTL(fd, VIDIOC_ENUM_FMT, &fmtdesc); + if (ret < 0) + break; + + //TODO: Is there any better way to detect it? + + format.type = type; + format.fmt.pix.pixelformat = fmtdesc.pixelformat; + + /* Allow error since not all the drivers support try_fmt */ + SYS_IOCTL(fd, VIDIOC_TRY_FMT, &format); + + switch (format.fmt.pix_mp.num_planes) { + case 1: + if (formats->def_format < 0) + formats->def_format = formats->num_formats; + + /* fall-through */ + case 0: + /** + * Allow 0 planes since not all the drivers would set + * num_planes in try_fmt. + */ + formats->formats[formats->num_formats] = format; + formats->index_map[formats->num_formats] = n; + formats->num_formats++; + break; + default: + break; + } + } +} + static void *plugin_init(int fd) { struct v4l2_capability cap; @@ -92,13 +153,21 @@ static void *plugin_init(int fd) } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && - (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) + (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) { plugin.mplane_capture = 1; + mplane_setup_formats(fd, &plugin.capture_formats, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + } + if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && - (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) + (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)) { plugin.mplane_output = 1; + mplane_setup_formats(fd, &plugin.output_formats, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + } + /* Device doesn't need it. return NULL to disable the plugin */ if (!plugin.mplane) return NULL; @@ -175,6 +244,39 @@ static int convert_type(int type) } } +static int enum_fmt_ioctl(struct mplane_plugin *plugin, int fd, + unsigned long int cmd, + struct v4l2_fmtdesc *arg) +{ + struct mplane_formats *formats; + int ret, index; + + switch (arg->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + formats = &plugin->capture_formats; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + formats = &plugin->output_formats; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + errno = EINVAL; + return -1; + default: + return SYS_IOCTL(fd, cmd, arg); + } + + if (arg->index >= formats->num_formats) + return -EINVAL; + + index = arg->index; + arg->index = formats->index_map[index]; + ret = SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_fmtdesc); + arg->index = index; + + return ret; +} + static void sanitize_format(struct v4l2_format *fmt) { unsigned int offset; @@ -201,19 +303,23 @@ static void sanitize_format(struct v4l2_format *fmt) sizeof(fmt->fmt.pix) - offset); } -static int try_set_fmt_ioctl(int fd, unsigned long int cmd, +static int try_set_fmt_ioctl(struct mplane_plugin *plugin, int fd, + unsigned long int cmd, struct v4l2_format *arg) { + struct mplane_formats *formats; struct v4l2_format fmt = { 0 }; struct v4l2_format *org = arg; - int ret; + int ret, i; switch (arg->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + formats = &plugin->capture_formats; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + formats = &plugin->output_formats; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -223,6 +329,15 @@ static int try_set_fmt_ioctl(int fd, unsigned long int cmd, return SYS_IOCTL(fd, cmd, arg); } + /* Filter out unsupported formats */ + for (i = 0; i < formats->num_formats; i++) { + if (formats->formats[i].fmt.pix_mp.pixelformat == + arg->fmt.pix.pixelformat) + break; + } + if (i == formats->num_formats) + return -EINVAL; + sanitize_format(org); fmt.fmt.pix_mp.width = org->fmt.pix.width; @@ -254,6 +369,10 @@ static int try_set_fmt_ioctl(int fd, unsigned long int cmd, org->fmt.pix.sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage; org->fmt.pix.flags = fmt.fmt.pix_mp.flags; + /* Now we can use the driver's default format */ + if (cmd == VIDIOC_S_FMT) + formats->def_format = -1; + return 0; } @@ -316,8 +435,10 @@ static int create_bufs_ioctl(int fd, unsigned long int cmd, return ret; } -static int get_fmt_ioctl(int fd, unsigned long int cmd, struct v4l2_format *arg) +static int get_fmt_ioctl(struct mplane_plugin *plugin, int fd, + unsigned long int cmd, struct v4l2_format *arg) { + struct mplane_formats *formats; struct v4l2_format fmt = { 0 }; struct v4l2_format *org = arg; int ret; @@ -325,9 +446,11 @@ static int get_fmt_ioctl(int fd, unsigned long int cmd, struct v4l2_format *arg) switch (arg->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + formats = &plugin->capture_formats; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + formats = &plugin->output_formats; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -337,10 +460,18 @@ static int get_fmt_ioctl(int fd, unsigned long int cmd, struct v4l2_format *arg) return SYS_IOCTL(fd, cmd, arg); } + /* Use default format */ + if (formats->def_format >= 0 && + formats->def_format < formats->num_formats) { + fmt = formats->formats[formats->def_format]; + goto out; + } + ret = SYS_IOCTL(fd, cmd, &fmt); if (ret) return ret; +out: memset(&org->fmt.pix, 0, sizeof(org->fmt.pix)); org->fmt.pix.width = fmt.fmt.pix_mp.width; org->fmt.pix.height = fmt.fmt.pix_mp.height; @@ -411,16 +542,18 @@ static int buf_ioctl(int fd, unsigned long int cmd, struct v4l2_buffer *arg) static int plugin_ioctl(void *dev_ops_priv, int fd, unsigned long int cmd, void *arg) { + struct mplane_plugin *plugin = dev_ops_priv; + switch (cmd) { case VIDIOC_QUERYCAP: return querycap_ioctl(fd, cmd, arg); case VIDIOC_TRY_FMT: case VIDIOC_S_FMT: - return try_set_fmt_ioctl(fd, cmd, arg); + return try_set_fmt_ioctl(plugin, fd, cmd, arg); case VIDIOC_G_FMT: - return get_fmt_ioctl(fd, cmd, arg); + return get_fmt_ioctl(plugin, fd, cmd, arg); case VIDIOC_ENUM_FMT: - return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_fmtdesc); + return enum_fmt_ioctl(plugin, fd, cmd, arg); case VIDIOC_S_PARM: case VIDIOC_G_PARM: return SIMPLE_CONVERT_IOCTL(fd, cmd, arg, v4l2_streamparm); -- 2.11.0