Rockchip: Store cropped size in v4l plugin.

When setting the format of OUTPUT queue, the rockchip driver may
increase the height 16 to align the buffer. For example, when setting
QCIF(176x144) size, the driver will adjust to 176x160. However, the
driver doesn't accept the cropped size with larger than 16 (1 MB).
That means, we cannot use crop to set the correct resolution.

This CL stores the cropped size at the plugin, and uses it as the
resolution of the encoded video. Regardless there are some restriction
about the cropped size at the driver, the plugin allow any cropped
size.

BUG=b:79551489
TEST=pass VEA unittest with QCIF video.
TEST=pass testEncodeDecodeVideoFromBufferToBufferQCIF CTS

Change-Id: Iabfb80aa7c5bff357e91661bd3ede581b5aef83a
Reviewed-on: https://chromium-review.googlesource.com/1061035
Commit-Ready: Chih-Yu Huang <akahuang@chromium.org>
Tested-by: Chih-Yu Huang <akahuang@chromium.org>
Reviewed-by: Tomasz Figa <tfiga@chromium.org>
diff --git a/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c b/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c
index 28ababa..46800e5 100644
--- a/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c
+++ b/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c
@@ -47,6 +47,7 @@
 #define DEFAULT_FRAME_RATE 30
 #define DEFAULT_BITRATE 1000000
 #define PENDING_BUFFER_QUEUE_SIZE VIDEO_MAX_FRAME
+#define round_down(x, y) ((x) - ((x) % (y)))
 
 /*
  * struct pending_buffer - A v4l2 buffer pending for QBUF.
@@ -100,6 +101,17 @@
  * @flushing:	Indicate the encoder is flushing frame.
  * @eos_buffer:	The last buffer to be flushed. Reset to NULL after the buffer
  *		is enqueued to the driver.
+ * @output_format:	The compressed format of the encoded video.
+ * @frame_width:	The width of the input frame.
+ * @frame_height:	The height of the input frame.
+ * @crop_width:	The width of the cropped rectangle. The value is set when
+ * 		VIDIOC_S_FMT or VIDIOC_S_CROP for OUTPUT queue is called
+ * 		successfully, and is used as the width of encoded video.
+ * 		Note: we store the original value passed from ioctl, instead of
+ * 		the value adjusted by the driver. Also, the value should be
+ * 		smaller or equal than frame_width.
+ * @crop_height:	The height of the croped rectangle. Other description is
+ *			the same as crop_width.
  */
 struct encoder_context {
 	void *enc;
@@ -115,6 +127,11 @@
 	struct v4l2_ext_control v4l2_ctrls[MAX_NUM_GET_CONFIG_CTRLS];
 	bool flushing;
 	struct pending_buffer *eos_buffer;
+	uint32_t output_format;
+	uint32_t frame_width;
+	uint32_t frame_height;
+	uint32_t crop_width;
+	uint32_t crop_height;
 };
 
 static void *plugin_init(int fd);
@@ -135,6 +152,12 @@
 	struct v4l2_ext_controls *ext_ctrls);
 static int ioctl_s_parm_locked(struct encoder_context *ctx, int fd,
 	struct v4l2_streamparm *parms);
+static int ioctl_s_fmt_locked(struct encoder_context *ctx, int fd,
+	struct v4l2_format *format);
+static int ioctl_s_crop_locked(struct encoder_context *ctx, int fd,
+	struct v4l2_crop *crop);
+static int ioctl_g_crop_locked(struct encoder_context *ctx, int fd,
+	struct v4l2_crop *crop);
 static int ioctl_reqbufs_locked(struct encoder_context *ctx, int fd,
 	struct v4l2_requestbuffers *reqbufs);
 static int ioctl_encoder_cmd_locked(struct encoder_context *ctx, int fd,
@@ -270,6 +293,18 @@
 		ret = ioctl_s_parm_locked(ctx, fd, arg);
 		break;
 
+	case VIDIOC_S_FMT:
+		ret = ioctl_s_fmt_locked(ctx, fd, arg);
+		break;
+
+	case VIDIOC_S_CROP:
+		ret = ioctl_s_crop_locked(ctx, fd, arg);
+		break;
+
+	case VIDIOC_G_CROP:
+		ret = ioctl_g_crop_locked(ctx, fd, arg);
+		break;
+
 	case VIDIOC_REQBUFS:
 		ret = ioctl_reqbufs_locked(ctx, fd, arg);
 		break;
@@ -498,6 +533,76 @@
 	return SYS_IOCTL(fd, VIDIOC_S_PARM, parms);
 }
 
+static int ioctl_s_fmt_locked(struct encoder_context *ctx, int fd,
+		struct v4l2_format *format)
+{
+	uint32_t width = format->fmt.pix_mp.width;
+	uint32_t height = format->fmt.pix_mp.height;
+	int ret = SYS_IOCTL(fd, VIDIOC_S_FMT, format);
+	if (ret)
+		return ret;
+	if (!V4L2_TYPE_IS_OUTPUT(format->type)) {
+		ctx->output_format = format->fmt.pix_mp.pixelformat;
+		return 0;
+	}
+
+	/* The width and height of H264 video should be even. */
+	if (ctx->output_format == V4L2_PIX_FMT_H264) {
+		width = round_down(width, 2);
+		height = round_down(height, 2);
+	}
+	ctx->frame_width = width;
+	ctx->frame_height = height;
+	ctx->crop_width = width;
+	ctx->crop_height = height;
+	return 0;
+}
+
+static int ioctl_s_crop_locked(struct encoder_context *ctx, int fd,
+		struct v4l2_crop *crop)
+{
+	/*
+	 * We do not support offsets, and the crop size should not be
+	 * bigger than the frame size. In this case, we reset the crop
+	 * size to full frame size.
+	 */
+	if (crop->c.left != 0 || crop->c.top != 0 ||
+			crop->c.width > ctx->frame_width ||
+			crop->c.height > ctx->frame_height) {
+		crop->c.left = 0;
+		crop->c.top = 0;
+		crop->c.width = ctx->frame_width;
+		crop->c.height = ctx->frame_height;
+	}
+
+	uint32_t width = crop->c.width;
+	uint32_t height = crop->c.height;
+	int ret = SYS_IOCTL(fd, VIDIOC_S_CROP, crop);
+	if (ret || !V4L2_TYPE_IS_OUTPUT(crop->type))
+		return ret;
+
+	/* The width and height of H264 video should be even. */
+	if (ctx->output_format == V4L2_PIX_FMT_H264) {
+		width = round_down(width, 2);
+		height = round_down(height, 2);
+	}
+	ctx->crop_width = width;
+	ctx->crop_height = height;
+	return 0;
+}
+
+static int ioctl_g_crop_locked(struct encoder_context *ctx, int fd,
+		struct v4l2_crop *crop)
+{
+	int ret = SYS_IOCTL(fd, VIDIOC_G_CROP, crop);
+	if (ret || !V4L2_TYPE_IS_OUTPUT(crop->type))
+		return ret;
+
+	crop->c.width = ctx->crop_width;
+	crop->c.height = ctx->crop_height;
+	return 0;
+}
+
 static int ioctl_reqbufs_locked(struct encoder_context *ctx, int fd,
 		struct v4l2_requestbuffers *reqbufs)
 {
@@ -623,14 +728,8 @@
 	init_param.output_format = format.fmt.pix_mp.pixelformat;
 
 	/* Get the cropped size. */
-	struct v4l2_crop crop;
-	memset(&crop, 0, sizeof(crop));
-	crop.type = ctx->output_streamon_type;
-	ret = SYS_IOCTL(fd, VIDIOC_G_CROP, &crop);
-	if (ret)
-		return ret;
-	init_param.width = crop.c.width;
-	init_param.height = crop.c.height;
+	init_param.width = ctx->crop_width;
+	init_param.height = ctx->crop_height;
 
 	init_param.h264e = ctx->init_param.h264e;