codecparsers: h265parser: Verify all possible profiles.
authorVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Tue, 14 Dec 2021 18:36:56 +0000 (19:36 +0100)
committerVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Thu, 16 Dec 2021 16:08:30 +0000 (17:08 +0100)
It's possible a HEVC stream to have multiple profiles given the
compatibility bits. Instead of returning a single profile, internal
gst_h265_profile_tier_level_get_profiles() returns an array with all
it possible profiles.

Profiles are appended into the array only if the generated profile
is not invalid.

gst_h265_profile_tier_level_get_profile() is rewritten in terms of
gst_h265_profile_tier_level_get_profiles(), returning the first
profile found the array.

And  gst_h265_get_profile_from_sps() is also rewritten in terms of
gst_h265_profile_tier_level_get_profiles(), but traversing the array
verifying if the proposed profile is actually valid by Annex A.3.x of
the specification.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1440>

subprojects/gst-plugins-bad/gst-libs/gst/codecparsers/gsth265parser.c

index 11bf6e7..dcd7c39 100644 (file)
@@ -3636,64 +3636,94 @@ static GstH265Profile
   return get_extension_profile (profiles, G_N_ELEMENTS (profiles), ptl);
 }
 
-/**
- * gst_h265_profile_tier_level_get_profile:
- * @ptl: a #GstH265ProfileTierLevel
- *
- * Return the H265 profile defined in @ptl.
- *
- * Returns: a #GstH265Profile
- * Since: 1.14
- */
-GstH265Profile
-gst_h265_profile_tier_level_get_profile (const GstH265ProfileTierLevel * ptl)
+static inline void
+append_profile (GstH265Profile profiles[GST_H265_PROFILE_MAX], guint * idx,
+    GstH265Profile profile)
+{
+  if (profile == GST_H265_PROFILE_INVALID)
+    return;
+  profiles[*idx++] = profile;
+}
+
+static void
+gst_h265_profile_tier_level_get_profiles (const GstH265ProfileTierLevel * ptl,
+    GstH265Profile profiles[GST_H265_PROFILE_MAX], guint * len)
 {
+  guint i = 0;
+
+  /* keep profile check in asc order */
+
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_MAIN
       || ptl->profile_compatibility_flag[1])
-    return GST_H265_PROFILE_MAIN;
+    profiles[i++] = GST_H265_PROFILE_MAIN;
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_MAIN_10
       || ptl->profile_compatibility_flag[2])
-    return GST_H265_PROFILE_MAIN_10;
+    profiles[i++] = GST_H265_PROFILE_MAIN_10;
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_MAIN_STILL_PICTURE
       || ptl->profile_compatibility_flag[3])
-    return GST_H265_PROFILE_MAIN_STILL_PICTURE;
+    profiles[i++] = GST_H265_PROFILE_MAIN_STILL_PICTURE;
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION
       || ptl->profile_compatibility_flag[4])
-    return get_format_range_extension_profile (ptl);
+    append_profile (profiles, &i, get_format_range_extension_profile (ptl));
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_HIGH_THROUGHPUT
       || ptl->profile_compatibility_flag[5])
-    return get_high_throughput_profile (ptl);
+    append_profile (profiles, &i, get_high_throughput_profile (ptl));
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_MULTIVIEW_MAIN
       || ptl->profile_compatibility_flag[6])
-    return get_multiview_profile (ptl);
+    append_profile (profiles, &i, get_multiview_profile (ptl));
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_SCALABLE_MAIN
       || ptl->profile_compatibility_flag[7])
-    return get_scalable_profile (ptl);
+    append_profile (profiles, &i, get_scalable_profile (ptl));
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_3D_MAIN
       || ptl->profile_compatibility_flag[8])
-    return get_3d_profile (ptl);
+    append_profile (profiles, &i, get_3d_profile (ptl));
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING
-      || ptl->profile_compatibility_flag[9])
-    return get_screen_content_coding_extensions_profile (ptl);
+      || ptl->profile_compatibility_flag[9]) {
+    append_profile (profiles, &i,
+        get_screen_content_coding_extensions_profile (ptl));
+  }
 
   if (ptl->profile_idc == GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION
-      || ptl->profile_compatibility_flag[10])
-    return get_scalable_format_range_extensions_profile (ptl);
+      || ptl->profile_compatibility_flag[10]) {
+    append_profile (profiles, &i,
+        get_scalable_format_range_extensions_profile (ptl));
+  }
 
   if (ptl->profile_idc ==
       GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION
-      || ptl->profile_compatibility_flag[11])
-    return get_screen_content_coding_extensions_high_throughput_profile (ptl);
+      || ptl->profile_compatibility_flag[11]) {
+    append_profile (profiles, &i,
+        get_screen_content_coding_extensions_high_throughput_profile (ptl));
+  }
 
-  return GST_H265_PROFILE_INVALID;
+  *len = i;
+}
+
+/**
+ * gst_h265_profile_tier_level_get_profile:
+ * @ptl: a #GstH265ProfileTierLevel
+ *
+ * Return the H265 profile defined in @ptl.
+ *
+ * Returns: a #GstH265Profile
+ * Since: 1.14
+ */
+GstH265Profile
+gst_h265_profile_tier_level_get_profile (const GstH265ProfileTierLevel * ptl)
+{
+  guint len;
+  GstH265Profile profiles[GST_H265_PROFILE_MAX] = { GST_H265_PROFILE_INVALID, };
+
+  gst_h265_profile_tier_level_get_profiles (ptl, profiles, &len);
+  return profiles[0];
 }
 
 /**
@@ -4318,78 +4348,106 @@ gst_h265_parser_insert_sei_hevc (GstH265Parser * parser, guint8 nal_length_size,
 GstH265Profile
 gst_h265_get_profile_from_sps (GstH265SPS * sps)
 {
-  GstH265Profile p;
-
-  p = gst_h265_profile_tier_level_get_profile (&sps->profile_tier_level);
-
-  if (p == GST_H265_PROFILE_INVALID) {
-    GstH265ProfileTierLevel tmp_ptl = sps->profile_tier_level;
-    guint chroma_format_idc = sps->chroma_format_idc;
-    guint bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
-    guint bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
-
-    /* Set the conformance indicators based on chroma_format_idc / bit_depth */
-    switch (chroma_format_idc) {
-      case 0:
-        tmp_ptl.max_monochrome_constraint_flag = 1;
-        tmp_ptl.max_420chroma_constraint_flag = 1;
-        tmp_ptl.max_422chroma_constraint_flag = 1;
-        break;
+  GstH265Profile profiles[GST_H265_PROFILE_MAX] = { GST_H265_PROFILE_INVALID, };
+  GstH265ProfileTierLevel tmp_ptl;
+  guint i, len = 0;
+  guint chroma_format_idc, bit_depth_luma, bit_depth_chroma;
 
-      case 1:
-        tmp_ptl.max_monochrome_constraint_flag = 0;
-        tmp_ptl.max_420chroma_constraint_flag = 1;
-        tmp_ptl.max_422chroma_constraint_flag = 1;
-        break;
+  g_return_val_if_fail (sps != NULL, GST_H265_PROFILE_INVALID);
 
-      case 2:
-        tmp_ptl.max_monochrome_constraint_flag = 0;
-        tmp_ptl.max_420chroma_constraint_flag = 0;
-        tmp_ptl.max_422chroma_constraint_flag = 1;
-        break;
+  tmp_ptl = sps->profile_tier_level;
+  chroma_format_idc = sps->chroma_format_idc;
+  bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
+  bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
 
-      case 3:
-        tmp_ptl.max_monochrome_constraint_flag = 0;
-        tmp_ptl.max_420chroma_constraint_flag = 0;
-        tmp_ptl.max_422chroma_constraint_flag = 0;
-        break;
+  gst_h265_profile_tier_level_get_profiles (&sps->profile_tier_level, profiles,
+      &len);
 
-      default:
-        g_assert_not_reached ();
+  for (i = 0; i < len && i < G_N_ELEMENTS (profiles); i++) {
+    switch (profiles[i]) {
+      case GST_H265_PROFILE_INVALID:
         break;
+      case GST_H265_PROFILE_MAIN:
+      case GST_H265_PROFILE_MAIN_STILL_PICTURE:
+        /* A.3.2 or A.3.5 */
+        if (chroma_format_idc == 1
+            && bit_depth_luma == 8 && bit_depth_chroma == 8)
+          return profiles[i];
+        break;
+      case GST_H265_PROFILE_MAIN_10:
+        /* A.3.3 */
+        if (chroma_format_idc == 1
+            && bit_depth_luma >= 8 && bit_depth_luma <= 10
+            && bit_depth_chroma >= 8 && bit_depth_chroma <= 10)
+          return profiles[i];
+        break;
+      default:
+        return profiles[i];
     }
+  }
 
-    tmp_ptl.max_8bit_constraint_flag = 1;
-    tmp_ptl.max_10bit_constraint_flag = 1;
-    tmp_ptl.max_12bit_constraint_flag = 1;
-    tmp_ptl.max_14bit_constraint_flag = 1;
-
-    if (bit_depth_luma > 8 || bit_depth_chroma > 8)
-      tmp_ptl.max_8bit_constraint_flag = 0;
-
-    if (bit_depth_luma > 10 || bit_depth_chroma > 10)
-      tmp_ptl.max_10bit_constraint_flag = 0;
-
-    if (bit_depth_luma > 12 || bit_depth_chroma > 12)
-      tmp_ptl.max_12bit_constraint_flag = 0;
-
-    if (tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_HIGH_THROUGHPUT
-        || tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING
-        || tmp_ptl.profile_idc ==
-        GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION
-        || tmp_ptl.profile_idc ==
-        GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION
-        || tmp_ptl.profile_compatibility_flag[5]
-        || tmp_ptl.profile_compatibility_flag[9]
-        || tmp_ptl.profile_compatibility_flag[10]
-        || tmp_ptl.profile_compatibility_flag[11]) {
-      if (bit_depth_luma > 14 || bit_depth_chroma > 14)
-        tmp_ptl.max_14bit_constraint_flag = 0;
-    } else
-      tmp_ptl.max_14bit_constraint_flag = 0;
+  /* Invalid profile: */
+  /* Set the conformance indicators based on chroma_format_idc / bit_depth */
+  switch (chroma_format_idc) {
+    case 0:
+      tmp_ptl.max_monochrome_constraint_flag = 1;
+      tmp_ptl.max_420chroma_constraint_flag = 1;
+      tmp_ptl.max_422chroma_constraint_flag = 1;
+      break;
+
+    case 1:
+      tmp_ptl.max_monochrome_constraint_flag = 0;
+      tmp_ptl.max_420chroma_constraint_flag = 1;
+      tmp_ptl.max_422chroma_constraint_flag = 1;
+      break;
+
+    case 2:
+      tmp_ptl.max_monochrome_constraint_flag = 0;
+      tmp_ptl.max_420chroma_constraint_flag = 0;
+      tmp_ptl.max_422chroma_constraint_flag = 1;
+      break;
+
+    case 3:
+      tmp_ptl.max_monochrome_constraint_flag = 0;
+      tmp_ptl.max_420chroma_constraint_flag = 0;
+      tmp_ptl.max_422chroma_constraint_flag = 0;
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  tmp_ptl.max_8bit_constraint_flag = 1;
+  tmp_ptl.max_10bit_constraint_flag = 1;
+  tmp_ptl.max_12bit_constraint_flag = 1;
+  tmp_ptl.max_14bit_constraint_flag = 1;
+
+  if (bit_depth_luma > 8 || bit_depth_chroma > 8)
+    tmp_ptl.max_8bit_constraint_flag = 0;
+
+  if (bit_depth_luma > 10 || bit_depth_chroma > 10)
+    tmp_ptl.max_10bit_constraint_flag = 0;
 
-    p = gst_h265_profile_tier_level_get_profile (&tmp_ptl);
+  if (bit_depth_luma > 12 || bit_depth_chroma > 12)
+    tmp_ptl.max_12bit_constraint_flag = 0;
+
+  if (tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_HIGH_THROUGHPUT
+      || tmp_ptl.profile_idc == GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING
+      || tmp_ptl.profile_idc ==
+      GST_H265_PROFILE_IDC_SCALABLE_FORMAT_RANGE_EXTENSION
+      || tmp_ptl.profile_idc ==
+      GST_H265_PROFILE_IDC_HIGH_THROUGHPUT_SCREEN_CONTENT_CODING_EXTENSION
+      || tmp_ptl.profile_compatibility_flag[5]
+      || tmp_ptl.profile_compatibility_flag[9]
+      || tmp_ptl.profile_compatibility_flag[10]
+      || tmp_ptl.profile_compatibility_flag[11]) {
+    if (bit_depth_luma > 14 || bit_depth_chroma > 14)
+      tmp_ptl.max_14bit_constraint_flag = 0;
+  } else {
+    tmp_ptl.max_14bit_constraint_flag = 0;
   }
 
-  return p;
+  /* first profile of the synthetic ptl */
+  return gst_h265_profile_tier_level_get_profile (&tmp_ptl);
 }