va: Add extended formats support such as 10/12 bits, 4:2:2 and 4:4:4.
authorHe Junyan <junyan.he@intel.com>
Mon, 2 May 2022 15:14:32 +0000 (23:14 +0800)
committerHe Junyan <junyan.he@intel.com>
Thu, 1 Dec 2022 01:45:14 +0000 (09:45 +0800)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2036>

subprojects/gst-plugins-bad/sys/va/gstvah265enc.c

index bd29586..5171aba 100644 (file)
@@ -566,8 +566,8 @@ _h265_fill_ptl (GstVaH265Enc * self,
   /* additional indications specified for general_profile_idc from 4~10 */
   if (sequence->general_profile_idc == 4) {
     /* In A.3.5, Format range extensions profiles.
-       Just support main444, main444-10 and main422-10 profile now, may add
-       more profiles when needed. */
+       Just support main444, main444-10 main422-10 main422-12 and main-12
+       profile now, may add more profiles when needed. */
     switch (base->profile) {
       case VAProfileHEVCMain444:
         ptl->max_12bit_constraint_flag = 1;
@@ -602,6 +602,17 @@ _h265_fill_ptl (GstVaH265Enc * self,
         ptl->one_picture_only_constraint_flag = 0;
         ptl->lower_bit_rate_constraint_flag = 1;
         break;
+      case VAProfileHEVCMain422_12:
+        ptl->max_12bit_constraint_flag = 1;
+        ptl->max_10bit_constraint_flag = 0;
+        ptl->max_8bit_constraint_flag = 0;
+        ptl->max_422chroma_constraint_flag = 1;
+        ptl->max_420chroma_constraint_flag = 0;
+        ptl->max_monochrome_constraint_flag = 0;
+        ptl->intra_constraint_flag = 0;
+        ptl->one_picture_only_constraint_flag = 0;
+        ptl->lower_bit_rate_constraint_flag = 1;
+        break;
       case VAProfileHEVCMain12:
         ptl->max_12bit_constraint_flag = 1;
         ptl->max_10bit_constraint_flag = 0;
@@ -2391,17 +2402,59 @@ gst_va_h265_enc_reset_state (GstVaBaseEnc * base)
 }
 
 static guint
-_h265_get_rtformat (GstVaH265Enc * self, GstVideoFormat format)
+_h265_get_rtformat (GstVaH265Enc * self, GstVideoFormat format,
+    guint * depth, guint * chrome)
 {
   guint chroma;
 
   chroma = gst_va_chroma_from_video_format (format);
 
-  /* Check whether the rtformat is supported. */
-  if (chroma != VA_RT_FORMAT_YUV420) {
-    GST_ERROR_OBJECT (self, "Unsupported chroma for video format: %s",
-        gst_video_format_to_string (format));
-    return 0;
+  switch (chroma) {
+    case VA_RT_FORMAT_YUV400:
+      *depth = 8;
+      *chrome = 0;
+      break;
+    case VA_RT_FORMAT_YUV420:
+      *depth = 8;
+      *chrome = 1;
+      break;
+    case VA_RT_FORMAT_YUV422:
+      *depth = 8;
+      *chrome = 2;
+      break;
+    case VA_RT_FORMAT_YUV444:
+      *depth = 8;
+      *chrome = 3;
+      break;
+    case VA_RT_FORMAT_YUV420_10:
+      *depth = 10;
+      *chrome = 1;
+      break;
+    case VA_RT_FORMAT_YUV422_10:
+      *depth = 10;
+      *chrome = 2;
+      break;
+    case VA_RT_FORMAT_YUV444_10:
+      *depth = 10;
+      *chrome = 3;
+      break;
+    case VA_RT_FORMAT_YUV420_12:
+      *depth = 12;
+      *chrome = 1;
+      break;
+    case VA_RT_FORMAT_YUV422_12:
+      *depth = 12;
+      *chrome = 2;
+      break;
+    case VA_RT_FORMAT_YUV444_12:
+      *depth = 12;
+      *chrome = 3;
+      break;
+    default:
+      chroma = 0;
+      GST_ERROR_OBJECT (self, "Unsupported chroma for video format: %s",
+          gst_video_format_to_string (format));
+      break;
   }
 
   return chroma;
@@ -2417,13 +2470,15 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
   VAProfile profile;
   guint rt_format;
   GstCaps *allowed_caps = NULL;
-  guint num_structures, i;
+  guint num_structures, i, j;
   GstStructure *structure;
   const GValue *v_profile;
-  GPtrArray *candidates = NULL;
-  gchar *profile_name;
+  GArray *caps_candidates = NULL;
+  GArray *chroma_candidates = NULL;
+  guint depth = 0, chrome = 0;
 
-  candidates = g_ptr_array_new_with_free_func (g_free);
+  caps_candidates = g_array_new (TRUE, TRUE, sizeof (VAProfile));
+  chroma_candidates = g_array_new (TRUE, TRUE, sizeof (VAProfile));
 
   /* First, check whether the downstream requires a specified profile. */
   allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (base));
@@ -2439,8 +2494,12 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
         continue;
 
       if (G_VALUE_HOLDS_STRING (v_profile)) {
-        profile_name = g_strdup (g_value_get_string (v_profile));
-        g_ptr_array_add (candidates, profile_name);
+        profile =
+            gst_va_profile_from_name (HEVC, g_value_get_string (v_profile));
+        if (profile == VAProfileNone)
+          continue;
+
+        g_array_append_val (caps_candidates, profile);
       } else if (GST_VALUE_HOLDS_LIST (v_profile)) {
         guint j;
 
@@ -2449,21 +2508,23 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
           if (!p)
             continue;
 
-          profile_name = g_strdup (g_value_get_string (p));
-          g_ptr_array_add (candidates, profile_name);
+          profile = gst_va_profile_from_name (HEVC, g_value_get_string (p));
+          if (profile == VAProfileNone)
+            continue;
+          g_array_append_val (caps_candidates, profile);
         }
       }
     }
   }
 
-  if (candidates->len == 0) {
+  if (caps_candidates->len == 0) {
     GST_ERROR_OBJECT (self, "No available profile in caps");
     ret = FALSE;
     goto out;
   }
 
   in_format = GST_VIDEO_INFO_FORMAT (&base->input_state->info);
-  rt_format = _h265_get_rtformat (self, in_format);
+  rt_format = _h265_get_rtformat (self, in_format, &depth, &chrome);
   if (!rt_format) {
     GST_ERROR_OBJECT (self, "unsupported video format %s",
         gst_video_format_to_string (in_format));
@@ -2471,18 +2532,58 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
     goto out;
   }
 
-  /* Find the suitable profile by features and check the HW
-   * support. */
+  /* To make the thing a little simple here, We only consider the bit
+     depth compatibility for each level. For example, we will consider
+     that Main-4:4:4-10 is able to contain 8 bits 4:4:4 streams, but
+     the we wiil not consider that it will contain 10 bits 4:2:0 stream. */
+  if (chrome == 3) {
+    /* 4:4:4 */
+    if (depth == 8) {
+      profile = VAProfileHEVCMain444;
+      g_array_append_val (chroma_candidates, profile);
+    }
 
-  /* Just use the first HW available profile and disable features if
-   * needed. */
-  profile_name = NULL;
-  for (i = 0; i < candidates->len; i++) {
-    profile_name = g_ptr_array_index (candidates, i);
-    profile = gst_va_profile_from_name (HEVC, profile_name);
-    if (profile == VAProfileNone)
-      continue;
+    if (depth <= 10) {
+      profile = VAProfileHEVCMain444_10;
+      g_array_append_val (chroma_candidates, profile);
+    }
+
+    if (depth <= 12) {
+      profile = VAProfileHEVCMain444_12;
+      g_array_append_val (chroma_candidates, profile);
+    }
+  } else if (chrome == 2) {
+    /* 4:2:2 */
+    if (depth <= 10) {
+      profile = VAProfileHEVCMain422_10;
+      g_array_append_val (chroma_candidates, profile);
+    }
+
+    if (depth <= 12) {
+      profile = VAProfileHEVCMain422_12;
+      g_array_append_val (chroma_candidates, profile);
+    }
+  } else if (chrome == 1 || chrome == 0) {
+    /* 4:2:0 or 4:0:0 */
+    if (depth == 8) {
+      profile = VAProfileHEVCMain;
+      g_array_append_val (chroma_candidates, profile);
+    }
+
+    if (depth <= 10) {
+      profile = VAProfileHEVCMain10;
+      g_array_append_val (chroma_candidates, profile);
+    }
 
+    if (depth <= 12) {
+      profile = VAProfileHEVCMain12;
+      g_array_append_val (chroma_candidates, profile);
+    }
+  }
+
+  /* Just use the first HW available profile in candidate. */
+  for (i = 0; i < chroma_candidates->len; i++) {
+    profile = g_array_index (chroma_candidates, VAProfile, i);
     if (!gst_va_encoder_has_profile (base->encoder, profile))
       continue;
 
@@ -2490,17 +2591,23 @@ _h265_decide_profile (GstVaH265Enc * self, VAProfile * _profile,
                 profile, GST_VA_BASE_ENC_ENTRYPOINT (base))) == 0)
       continue;
 
+    for (j = 0; j < caps_candidates->len; j++) {
+      VAProfile p = g_array_index (caps_candidates, VAProfile, j);
+      if (profile == p)
+        break;
+    }
+    if (j == caps_candidates->len)
+      continue;
+
     *_profile = profile;
     *_rt_format = rt_format;
     ret = TRUE;
     goto out;
   }
 
-  if (ret == FALSE)
-    goto out;
-
 out:
-  g_clear_pointer (&candidates, g_ptr_array_unref);
+  g_clear_pointer (&caps_candidates, g_array_unref);
+  g_clear_pointer (&chroma_candidates, g_array_unref);
   g_clear_pointer (&allowed_caps, gst_caps_unref);
 
   if (ret) {
@@ -3068,16 +3175,35 @@ _h265_ensure_rate_control (GstVaH265Enc * self)
   if ((self->rc.rc_ctrl_mode == VA_RC_CBR || self->rc.rc_ctrl_mode == VA_RC_VBR
           || self->rc.rc_ctrl_mode == VA_RC_VCM) && bitrate == 0) {
     /* FIXME: Provide better estimation. */
-    /* Just Using a 1/6 compression ratio, 12 bits per pixel for YUV420.
-       TODO: Other video format. */
+    /* Choose the max value of all levels' MinCr which is 8, and x2 for
+       conservative calculation. So just using a 1/16 compression ratio,
+       and the bits per pixel for YUV420, YUV422, YUV444, accordingly. */
     guint64 factor;
+    guint depth = 8, chrome = 1;
+    guint bits_per_pix;
 
-    factor = (guint64) self->luma_width * self->luma_height * 12 / 6;
+    if (!_h265_get_rtformat (self,
+            GST_VIDEO_INFO_FORMAT (&base->input_state->info), &depth, &chrome))
+      g_assert_not_reached ();
+
+    if (chrome == 3) {
+      bits_per_pix = 24;
+    } else if (chrome == 2) {
+      bits_per_pix = 16;
+    } else {
+      bits_per_pix = 12;
+    }
+    bits_per_pix = bits_per_pix + bits_per_pix * (depth - 8) / 8;
+
+    factor = (guint64) self->luma_width * self->luma_height * bits_per_pix / 16;
     bitrate = gst_util_uint64_scale (factor,
         GST_VIDEO_INFO_FPS_N (&base->input_state->info),
         GST_VIDEO_INFO_FPS_D (&base->input_state->info)) / 1000;
 
     GST_INFO_OBJECT (self, "target bitrate computed to %u kbps", bitrate);
+
+    self->prop.bitrate = bitrate;
+    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BITRATE]);
   }
 
   /* Adjust the setting based on RC mode. */
@@ -3372,11 +3498,40 @@ static void
 _h265_calculate_coded_size (GstVaH265Enc * self)
 {
   GstVaBaseEnc *base = GST_VA_BASE_ENC (self);
+  guint codedbuf_size = 0;
+  guint chrome, depth;
+
+  if (!_h265_get_rtformat (self,
+          GST_VIDEO_INFO_FORMAT (&base->input_state->info), &depth, &chrome))
+    g_assert_not_reached ();
+
+  switch (chrome) {
+    case 0:
+      /* 4:0:0 */
+    case 1:
+      /* 4:2:0 */
+      codedbuf_size = (self->luma_width * self->luma_height * 3 / 2);
+      break;
+    case 2:
+      /* 4:2:2 */
+      codedbuf_size = (self->luma_width * self->luma_height * 2);
+      break;
+    case 3:
+      /* 4:4:4 */
+      codedbuf_size = (self->luma_width * self->luma_height * 3);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  codedbuf_size = codedbuf_size + (codedbuf_size * (depth - 8) / 8);
+  codedbuf_size = codedbuf_size / (self->min_cr / 2 /* For safety */ );
+
   /* FIXME: Using only a rough approximation for bitstream headers.
    * Not taken into account: ScalingList, RefPicListModification,
    * PredWeightTable, which is not used now. */
   /* Calculate the maximum sizes for common headers (in bits) */
-  guint codedbuf_size = 0;
 
   /* Account for VPS header */
   codedbuf_size += 4 /* start code */  + GST_ROUND_UP_8 (MAX_VPS_HDR_SIZE +
@@ -3394,11 +3549,6 @@ _h265_calculate_coded_size (GstVaH265Enc * self)
   codedbuf_size += self->partition.num_slices * (4 +
       GST_ROUND_UP_8 (MAX_SLICE_HDR_SIZE + MAX_SHORT_TERM_REFPICSET_SIZE) / 8);
 
-  /* TODO: Only YUV 4:2:0 formats are supported for now.
-     more video format to check. */
-  codedbuf_size +=
-      (self->luma_width * self->luma_height * 3 / 2) / self->min_cr;
-
   base->codedbuf_size = codedbuf_size;
   GST_INFO_OBJECT (self, "Calculate codedbuf size: %u", base->codedbuf_size);
 }