meta-digi/meta-digi-arm/dynamic-layers/stm-st-stm32mp/recipes-multimedia/gstreamer/gstreamer1.0-plugins-bad/0066-v4l2codecs-h264enc-Rew...

287 lines
12 KiB
Diff

From 5d051f38c95bb6f2f72588d55696a14939bc5fb5 Mon Sep 17 00:00:00 2001
From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Date: Thu, 30 Nov 2023 10:49:57 -0500
Subject: [PATCH 66/68] v4l2codecs: h264enc: Rewrite profile/level negotiation
This aligns the SPS/PPS with caps and properly negotiate the level.
Upstream-Status: Pending
---
sys/v4l2codecs/gstv4l2codech264enc.c | 213 ++++++++++++++++-----------
1 file changed, 124 insertions(+), 89 deletions(-)
diff --git a/sys/v4l2codecs/gstv4l2codech264enc.c b/sys/v4l2codecs/gstv4l2codech264enc.c
index 7d52fba..911ed34 100644
--- a/sys/v4l2codecs/gstv4l2codech264enc.c
+++ b/sys/v4l2codecs/gstv4l2codech264enc.c
@@ -80,10 +80,11 @@ struct _GstV4l2CodecH264Enc
gint width_in_macroblocks;
gint height_in_macroblocks;
gint qp_init, qp_max, qp_min;
- guint64 targeted_bitrate;
gboolean cabac;
guint cabac_init_idc;
+
gchar *profile_name;
+ guint level_idc;
GstV4l2CodecAllocator *sink_allocator;
GstV4l2CodecAllocator *src_allocator;
@@ -387,56 +388,10 @@ get_sps_aspect_ratio_idc (guint par_n, guint par_d)
return 255; // Extended_SAR for custom ratios
}
-/* Begin of code taken from VA plugin */
-typedef struct
-{
- const char *level;
- int idc;
- int max_macroblocks_per_second;
- int max_frame_size_in_macroblocks;
- int max_dpb_macroblocks;
- int maximum_bitrate;
- int max_cpb_size;
- int minimum_compression_ratio;
-} GstVaH264LevelLimits;
-
-static const GstVaH264LevelLimits _h264_level_limits[] = {
- /* *INDENT-OFF* */
- /*level, idc, max_macroblocks_per_second, max_frame_size_in_macroblocks,
- max_dpb_macroblocks, max_bitrate, max_cpb_size, minimum_compression_ratio */
- {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2},
- {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2},
- {"1.1", GST_H264_LEVEL_L1_1, 3000, 396, 900, 192, 500, 2},
- {"1.2", GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384, 1000, 2},
- {"1.3", GST_H264_LEVEL_L1_3, 11880, 396, 2376, 768, 2000, 2},
- {"2", GST_H264_LEVEL_L2, 11880, 396, 2376, 2000, 2000, 2},
- {"2.1", GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000, 4000, 2},
- {"2.2", GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000, 4000, 2},
- {"3", GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000, 10000, 2},
- {"3.1", GST_H264_LEVEL_L3_1, 108000, 3600, 18000, 14000, 14000, 4},
- {"3.2", GST_H264_LEVEL_L3_2, 216000, 5120, 20480, 20000, 20000, 4},
- {"4", GST_H264_LEVEL_L4, 245760, 8192, 32768, 20000, 25000, 4},
- {"4.1", GST_H264_LEVEL_L4_1, 245760, 8192, 32768, 50000, 62500, 2},
- {"4.2", GST_H264_LEVEL_L4_2, 522240, 8704, 34816, 50000, 62500, 2},
- {"5", GST_H264_LEVEL_L5, 589824, 22080, 110400, 135000, 135000, 2},
- {"5.1", GST_H264_LEVEL_L5_1, 983040, 36864, 184320, 240000, 240000, 2},
- {"5.2", GST_H264_LEVEL_L5_2, 2073600, 36864, 184320, 240000, 240000, 2},
- {"6", GST_H264_LEVEL_L6, 4177920, 139264, 696320, 240000, 240000, 2},
- {"6.1", GST_H264_LEVEL_L6_1, 8355840, 139264, 696320, 480000, 480000, 2},
- {"6.2", GST_H264_LEVEL_L6_2, 16711680, 139264, 696320, 800000, 800000, 2},
- /* *INDENT-ON* */
-};
-
-/* Enf of code taken from VA Plugin */
-
static void
gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self,
GstVideoCodecState * state)
{
- gint maximum_bitrate, frame_size_in_macroblocks,
- macroblocks_per_second, dpb_macroblocks, cpb_size, compression_ratio,
- minimum_level_index;
-
memset (&self->sps, 0, sizeof (self->sps));
memset (&self->sps, 0, sizeof (self->pps));
@@ -474,41 +429,10 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self,
self->sps.direct_8x8_inference_flag = 1;
self->sps.frame_mbs_only_flag = 1;
- // Calculate lowest acceptable level
- gint n_levels = G_N_ELEMENTS (_h264_level_limits);
- GValue bitrate = G_VALUE_INIT;
- g_object_get_property (G_OBJECT (self), "bitrate", &bitrate);
- self->targeted_bitrate = g_value_get_uint (&bitrate);
- maximum_bitrate = self->targeted_bitrate;
- frame_size_in_macroblocks = self->width * self->height / 256;
- macroblocks_per_second = frame_size_in_macroblocks * state->info.fps_n / state->info.fps_d; // Assuming each macroblock is 16x16
- dpb_macroblocks = frame_size_in_macroblocks;
- cpb_size = maximum_bitrate;
- compression_ratio = state->info.fps_n / state->info.fps_d;
- minimum_level_index = 0;
- for (; minimum_level_index < n_levels; ++minimum_level_index) {
- const GstVaH264LevelLimits *level =
- &_h264_level_limits[minimum_level_index];
- if (macroblocks_per_second <= level->max_macroblocks_per_second
- && frame_size_in_macroblocks <= level->max_frame_size_in_macroblocks
- && dpb_macroblocks <= level->max_dpb_macroblocks
- && maximum_bitrate <= level->maximum_bitrate
- && cpb_size <= level->max_cpb_size
- && compression_ratio >= level->minimum_compression_ratio) {
- break;
- }
- }
- self->sps.level_idc = _h264_level_limits[minimum_level_index].idc;
- if (self->sps.level_idc == GST_H264_LEVEL_L1B) {
+ /* Add level specific constraint */
+ self->sps.level_idc = self->level_idc;
+ if (self->sps.level_idc == GST_H264_LEVEL_L1B)
self->sps.constraint_set3_flag = 1;
- }
- GST_DEBUG_OBJECT (self, "width:%d height:%d targeted_bitrate:%ld",
- self->height, self->height, self->targeted_bitrate);
- GST_DEBUG_OBJECT (self,
- "macroblocks_per_second=%d,frame_size_in_macroblocks=%d,dpb_macroblocks=%d,maximum_bitrate=%d,cpb_size=%d,compression_ratio=%d",
- macroblocks_per_second, frame_size_in_macroblocks, dpb_macroblocks,
- maximum_bitrate, cpb_size, compression_ratio);
- GST_DEBUG_OBJECT (self, "level_idc: %d", self->sps.level_idc);
/* Crop unaligned videos */
if (self->width & 15 || self->height & 15) {
@@ -582,13 +506,57 @@ gst_v4l2_codec_h264_enc_init_sps_pps (GstV4l2CodecH264Enc * self,
self->pps.entropy_coding_mode_flag = self->cabac;
}
-static gchar *
-gst_v4l2_codec_h264_enc_decide_profile (GstV4l2CodecH264Enc * self)
+/* Begin of code taken from VA plugin */
+typedef struct
+{
+ const char *level;
+ int idc;
+ int max_mbs_per_second;
+ int max_frame_size_in_mbs;
+ int max_dpb_mbs;
+ int max_bitrate;
+ int max_cpb_size;
+ int minimum_compression_ratio;
+} GstH264LevelLimits;
+
+static const GstH264LevelLimits _h264_level_limits[] = {
+ /* *INDENT-OFF* */
+ // level, idc, mbs, frame, dpb, bitrate, cpb, cr
+ {"1", GST_H264_LEVEL_L1, 1485, 99, 396, 64, 175, 2},
+ {"1b", GST_H264_LEVEL_L1B, 1485, 99, 396, 128, 350, 2},
+ {"1.1", GST_H264_LEVEL_L1_1, 3000, 396, 900, 192, 500, 2},
+ {"1.2", GST_H264_LEVEL_L1_2, 6000, 396, 2376, 384, 1000, 2},
+ {"1.3", GST_H264_LEVEL_L1_3, 11880, 396, 2376, 768, 2000, 2},
+ {"2", GST_H264_LEVEL_L2, 11880, 396, 2376, 2000, 2000, 2},
+ {"2.1", GST_H264_LEVEL_L2_1, 19800, 792, 4752, 4000, 4000, 2},
+ {"2.2", GST_H264_LEVEL_L2_2, 20250, 1620, 8100, 4000, 4000, 2},
+ {"3", GST_H264_LEVEL_L3, 40500, 1620, 8100, 10000, 10000, 2},
+ {"3.1", GST_H264_LEVEL_L3_1, 108000, 3600, 18000, 14000, 14000, 4},
+ {"3.2", GST_H264_LEVEL_L3_2, 216000, 5120, 20480, 20000, 20000, 4},
+ {"4", GST_H264_LEVEL_L4, 245760, 8192, 32768, 20000, 25000, 4},
+ {"4.1", GST_H264_LEVEL_L4_1, 245760, 8192, 32768, 50000, 62500, 2},
+ {"4.2", GST_H264_LEVEL_L4_2, 522240, 8704, 34816, 50000, 62500, 2},
+ {"5", GST_H264_LEVEL_L5, 589824, 22080, 110400, 135000, 135000, 2},
+ {"5.1", GST_H264_LEVEL_L5_1, 983040, 36864, 184320, 240000, 240000, 2},
+ {"5.2", GST_H264_LEVEL_L5_2, 2073600, 36864, 184320, 240000, 240000, 2},
+ {"6", GST_H264_LEVEL_L6, 4177920, 139264, 696320, 240000, 240000, 2},
+ {"6.1", GST_H264_LEVEL_L6_1, 8355840, 139264, 696320, 480000, 480000, 2},
+ {"6.2", GST_H264_LEVEL_L6_2, 16711680, 139264, 696320, 800000, 800000, 2},
+ /* *INDENT-ON* */
+};
+
+/* Enf of code taken from VA Plugin */
+
+static gboolean
+gst_v4l2_codec_h264_enc_decide_profile_and_level (GstV4l2CodecH264Enc * self,
+ GstVideoCodecState * state)
{
GstCaps *allowed_caps = NULL;
const gchar *profile_name;
+ const gchar *level_name;
GstStructure *structure;
- gchar *ret;
+ gint minimum_level_index, caps_level_index;
+ guint bitrate, frame_size_in_mbs, mbs_per_second, dpb_mbs;
g_object_get (self, "cabac", &self->cabac, "cabac-init-idc",
&self->cabac_init_idc, NULL);
@@ -613,10 +581,77 @@ gst_v4l2_codec_h264_enc_decide_profile (GstV4l2CodecH264Enc * self)
}
}
- ret = g_strdup (profile_name);
- gst_caps_unref (allowed_caps);
+ g_free (self->profile_name);
+ self->profile_name = g_strdup (profile_name);
- return ret;
+ /* Calculate lowest acceptable level */
+ g_object_get (self, "bitrate", &bitrate, NULL);
+ if (bitrate == G_MAXUINT)
+ bitrate = 0;
+
+ /* table is in Kb/s, translate that number */
+ bitrate /= 1000;
+
+ frame_size_in_mbs = self->width_in_macroblocks * self->height_in_macroblocks;
+ mbs_per_second = frame_size_in_mbs * state->info.fps_n / state->info.fps_d;
+
+ /* Only one reference is supported at the moment */
+ dpb_mbs = frame_size_in_mbs;
+
+ /* Ignoring CPB as we don't implement HRD */
+
+ for (minimum_level_index = 0;
+ minimum_level_index < G_N_ELEMENTS (_h264_level_limits);
+ minimum_level_index++) {
+ const GstH264LevelLimits *level = &_h264_level_limits[minimum_level_index];
+ if (mbs_per_second <= level->max_mbs_per_second
+ && frame_size_in_mbs <= level->max_frame_size_in_mbs
+ && dpb_mbs <= level->max_dpb_mbs && bitrate <= level->max_bitrate) {
+ break;
+ }
+ }
+
+ if (minimum_level_index >= G_N_ELEMENTS (_h264_level_limits)) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, ("Unsupported H.264 level."),
+ ("The minimum level required for this stream is not supported."));
+ gst_caps_unref (allowed_caps);
+ return FALSE;
+ }
+
+ level_name = gst_structure_get_string (structure, "level");
+ caps_level_index = 0;
+ if (level_name) {
+ for (; caps_level_index < G_N_ELEMENTS (_h264_level_limits);
+ caps_level_index++) {
+ const GstH264LevelLimits *level = &_h264_level_limits[caps_level_index];
+ if (g_str_equal (level->level, level_name))
+ break;
+ }
+
+ if (caps_level_index >= G_N_ELEMENTS (_h264_level_limits)) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+ ("Unsupported H.264 level '%s'.", level_name),
+ ("The H.264 level provided in caps is not supported."));
+ gst_caps_unref (allowed_caps);
+ return FALSE;
+ }
+
+ if (caps_level_index < minimum_level_index) {
+ GST_ELEMENT_ERROR (self, CORE, NEGOTIATION,
+ ("H.264 level '%s' too low. At least '%s' is needed.", level_name,
+ _h264_level_limits[minimum_level_index].level),
+ ("The H.264 level provided in caps is too low."));
+ gst_caps_unref (allowed_caps);
+ return FALSE;
+ }
+
+ self->level_idc = _h264_level_limits[caps_level_index].idc;
+ } else {
+ self->level_idc = _h264_level_limits[minimum_level_index].idc;
+ }
+
+ gst_caps_unref (allowed_caps);
+ return TRUE;
}
static gboolean
@@ -663,8 +698,8 @@ gst_v4l2_codec_h264_enc_set_format (GstVideoEncoder * encoder,
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
- g_free (self->profile_name);
- self->profile_name = gst_v4l2_codec_h264_enc_decide_profile (self);
+ if (!gst_v4l2_codec_h264_enc_decide_profile_and_level (self, state))
+ return FALSE;
caps = gst_caps_new_simple ("video/x-h264",
"stream-format", G_TYPE_STRING, "byte-stream",
--
2.25.1