libv4lplugins: Change h264 level to a specified one

An app can specify the h264 stream of the encoded stream by
calling VIDIOC_S_EXT_CTRL with V4L2_CID_MPEG_VIDEO_H264_LEVEL.
However, libv4lplugins ignores the specified level the h264 level
is always 4.0. The plugin must change the level to the level
set by app.

BUG=chromium:1036219, chromium:1036220, b:146854692
TEST=webrtc.RTCPeerConnectionAccelUsed.enc_h264 on kevin
TEST=android.media.cts.MediaRecorderTest#testProfileAvcBaselineLevel1

Change-Id: I6c010af8862624e37418b86458a7f9df8aa2c868
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/libv4lplugins/+/1980599
Commit-Queue: Hirokazu Honda <hiroh@chromium.org>
Tested-by: Hirokazu Honda <hiroh@chromium.org>
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
Reviewed-by: Chih-Yu Huang <akahuang@chromium.org>
diff --git a/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c b/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c
index 3bf90ac..009c207 100644
--- a/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c
+++ b/libv4l-rockchip_v2/libv4l-encplugin-rockchip.c
@@ -199,6 +199,8 @@
 
 /* Returns true when the H264 profile is supported. */
 static bool is_supported_h264_profile(uint8_t profile);
+/* Returns true when the H264 true is supported. */
+static bool is_supported_h264_level(uint8_t level);
 
 static void *plugin_init(int fd)
 {
@@ -508,7 +510,13 @@
 		case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
 			if (!is_supported_h264_profile(ext_ctrls->controls[i].value))
 				return -EINVAL;
-			ctx->init_param.h264e.h264_profile =
+			ctx->init_param.h264e.v4l2_h264_profile =
+				ext_ctrls->controls[i].value;
+			break;
+		case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+			if (!is_supported_h264_level(ext_ctrls->controls[i].value))
+				return -EINVAL;
+			ctx->init_param.h264e.v4l2_h264_level =
 				ext_ctrls->controls[i].value;
 			break;
 		default:
@@ -770,6 +778,9 @@
 {
 	struct rk_vepu_init_param init_param;
 	memset(&init_param, 0, sizeof(init_param));
+	/* The default value of h264 level is 4.0. */
+	init_param.h264e.v4l2_h264_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
+
 
 	/* Get the input format. */
 	struct v4l2_format format;
@@ -939,6 +950,11 @@
 		profile == V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
 }
 
+static bool is_supported_h264_level(uint8_t level) {
+	// RK3399 is capable of encoding h264 whose level is 4.1 and lower.
+	return level <= V4L2_MPEG_VIDEO_H264_LEVEL_4_1;
+}
+
 PLUGIN_PUBLIC const struct libv4l_dev_ops libv4l2_plugin = {
 	.init = &plugin_init,
 	.close = &plugin_close,
diff --git a/libv4l-rockchip_v2/libvepu/h264e/h264e.c b/libv4l-rockchip_v2/libvepu/h264e/h264e.c
index 0362089..847cb6e 100644
--- a/libv4l-rockchip_v2/libvepu/h264e/h264e.c
+++ b/libv4l-rockchip_v2/libvepu/h264e/h264e.c
@@ -33,9 +33,50 @@
 const int baseline_idc = 66;
 const int main_idc = 77;
 
-static void h264e_init_sps(struct v4l2_plugin_h264_sps *sps, uint8_t profile)
+static uint8_t v4l2_level_to_h264_level(uint8_t level) {
+	switch (level) {
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+		return H264ENC_LEVEL_1;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+		return H264ENC_LEVEL_1_b;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+		return H264ENC_LEVEL_1_1;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+		return H264ENC_LEVEL_1_2;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+		return H264ENC_LEVEL_1_3;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+		return H264ENC_LEVEL_2;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+		return H264ENC_LEVEL_2_1;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+		return H264ENC_LEVEL_2_2;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+		return H264ENC_LEVEL_3;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+		return H264ENC_LEVEL_3_1;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+		return H264ENC_LEVEL_3_2;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+		return H264ENC_LEVEL_4_0;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+		return H264ENC_LEVEL_4_1;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+		return 42;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+		return 50;
+	case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+		return 51;
+	default:
+		fprintf(stderr, "Unknown h264 level=%d, use 4.0", (int) level);
+		return H264ENC_LEVEL_4_0;
+	};
+}
+
+
+static void h264e_init_sps(struct v4l2_plugin_h264_sps *sps, uint8_t v4l2_profile, uint8_t v4l2_level)
 {
-	switch(profile) {
+	switch(v4l2_profile) {
 	case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
 	case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
 		sps->profile_idc = baseline_idc;
@@ -54,9 +95,7 @@
 	/* if level idc == 1b, constraint_set3_flag need to be set true */
 	sps->constraint_set3_flag = 0;
 
-	/* if level max than 31, we prefer disable 4x4 mv mode
-	   to limit max mv count per 2mb */
-	sps->level_idc = H264ENC_LEVEL_4_0;
+	sps->level_idc = v4l2_level_to_h264_level(v4l2_level);
 	sps->seq_parameter_set_id = 0;
 
 	/* fixed values limited by hardware */
@@ -79,13 +118,13 @@
 	sps->vui_parameters_present_flag = 1;
 }
 
-static void h264e_init_pps(struct v4l2_plugin_h264_pps *pps, uint8_t profile)
+static void h264e_init_pps(struct v4l2_plugin_h264_pps *pps, uint8_t v4l2_profile)
 {
 	pps->pic_parameter_set_id = 0;
 	pps->seq_parameter_set_id = 0;
 
 	pps->entropy_coding_mode_flag = 0;
-	if (profile >= V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) {
+	if (v4l2_profile >= V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) {
 		/* enable cabac coding need set profile_idc large than 77 */
 		pps->entropy_coding_mode_flag = 1;
 	}
@@ -413,15 +452,15 @@
 	ictx->fmt = ENC_FORMAT_H264;
 
 	ctx->h264_sps_pps_before_idr = param->h264e.h264_sps_pps_before_idr;
-	ctx->h264_profile = param->h264e.h264_profile;
 
 	ctx->width = param->width;
 	ctx->height = param->height;
 	ctx->slice_size_mb_rows = 0;
 	ctx->frm_in_gop = 0;
 
-	h264e_init_sps(&ctx->sps, ctx->h264_profile);
-	h264e_init_pps(&ctx->pps, ctx->h264_profile);
+	h264e_init_sps(&ctx->sps, param->h264e.v4l2_h264_profile,
+		param->h264e.v4l2_h264_level);
+	h264e_init_pps(&ctx->pps, param->h264e.v4l2_h264_profile);
 	h264e_init_slice(&ctx->slice);
 
 	ctx->sps.pic_width_in_mbs = MB_COUNT(ctx->width);
@@ -441,6 +480,8 @@
 
 	h264e_init_rc(ictx);
 
+	/* if level is 31 or greater, we prefer disable 4x4 mv mode
+	   to limit max mv count per 2mb */
 	if (ctx->sps.level_idc >= H264ENC_LEVEL_3_1)
 		ctx->h264_inter4x4_disabled = 1;
 	else
diff --git a/libv4l-rockchip_v2/libvepu/h264e/h264e.h b/libv4l-rockchip_v2/libvepu/h264e/h264e.h
index c944527..32b8bf7 100644
--- a/libv4l-rockchip_v2/libvepu/h264e/h264e.h
+++ b/libv4l-rockchip_v2/libvepu/h264e/h264e.h
@@ -44,7 +44,6 @@
 	char stream_header[H264E_MAX_STREAM_HEADER_SIZE];
 	int stream_header_size;
 	int h264_sps_pps_before_idr;
-	uint8_t h264_profile;
 
 	int width;
 	int height;
diff --git a/libv4l-rockchip_v2/libvepu/rk_vepu_interface.h b/libv4l-rockchip_v2/libvepu/rk_vepu_interface.h
index 78457fe..31727ea 100644
--- a/libv4l-rockchip_v2/libvepu/rk_vepu_interface.h
+++ b/libv4l-rockchip_v2/libvepu/rk_vepu_interface.h
@@ -39,7 +39,8 @@
 	union {
 		struct {
 			bool h264_sps_pps_before_idr;
-			uint8_t h264_profile;
+			uint8_t v4l2_h264_profile;
+			uint8_t v4l2_h264_level;
 		} h264e;
 	};
 };