h265parse: add support for 'Format range extensions profiles'

Those profiles have been introduced in version 2 of the HEVC spec
(A.3.5).

https://bugzilla.gnome.org/show_bug.cgi?id=793876
diff --git a/gst-libs/gst/codecparsers/gsth265parser.c b/gst-libs/gst/codecparsers/gsth265parser.c
index 01f9d58..495f420 100644
--- a/gst-libs/gst/codecparsers/gsth265parser.c
+++ b/gst-libs/gst/codecparsers/gsth265parser.c
@@ -2642,6 +2642,88 @@
     out_quant[uprightdiagonal_8x8[i]] = quant[i];
 }
 
+typedef struct
+{
+  GstH265Profile profile;
+
+  guint8 max_12bit_constraint_flag;
+  guint8 max_10bit_constraint_flag;
+  guint8 max_8bit_constraint_flag;
+  guint8 max_422chroma_constraint_flag;
+  guint8 max_420chroma_constraint_flag;
+  guint8 max_monochrome_constraint_flag;
+  guint8 intra_constraint_flag;
+  guint8 one_picture_only_constraint_flag;
+  gboolean lower_bit_rate_constraint_flag_set;
+} FormatRangeExtensionProfile;
+
+static GstH265Profile
+get_format_range_extension_profile (GstH265ProfileTierLevel * ptl)
+{
+  /* See Table A.2 for the definition of those formats */
+  FormatRangeExtensionProfile profiles[] = {
+    {GST_H265_PROFILE_MONOCHROME, 1, 1, 1, 1, 1, 1, 0, 0, TRUE},
+    {GST_H265_PROFILE_MONOCHROME_12, 1, 0, 0, 1, 1, 1, 0, 0, TRUE},
+    {GST_H265_PROFILE_MONOCHROME_16, 0, 0, 0, 1, 1, 1, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_12, 1, 0, 0, 1, 1, 0, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_422_10, 1, 1, 0, 1, 0, 0, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_422_12, 1, 0, 0, 1, 0, 0, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_444, 1, 1, 1, 0, 0, 0, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_444_10, 1, 1, 0, 0, 0, 0, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_444_12, 1, 0, 0, 0, 0, 0, 0, 0, TRUE},
+    {GST_H265_PROFILE_MAIN_INTRA, 1, 1, 1, 1, 1, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_10_INTRA, 1, 1, 0, 1, 1, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_12_INTRA, 1, 0, 0, 1, 1, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_422_10_INTRA, 1, 1, 0, 1, 0, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_422_12_INTRA, 1, 0, 0, 1, 0, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_444_INTRA, 1, 1, 1, 0, 0, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_444_10_INTRA, 1, 1, 0, 0, 0, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_444_12_INTRA, 1, 0, 0, 0, 0, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_444_16_INTRA, 0, 0, 0, 0, 0, 0, 1, 0, FALSE},
+    {GST_H265_PROFILE_MAIN_444_STILL_PICTURE, 1, 1, 1, 0, 0, 0, 1, 1, FALSE},
+    {GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE, 0, 0, 0, 0, 0, 0, 1, 1, FALSE},
+  };
+  guint i;
+
+  for (i = 0; i < G_N_ELEMENTS (profiles); i++) {
+    FormatRangeExtensionProfile p = profiles[i];
+
+    if (p.max_12bit_constraint_flag != ptl->max_12bit_constraint_flag)
+      continue;
+    if (p.max_10bit_constraint_flag != ptl->max_10bit_constraint_flag)
+      continue;
+    if (p.max_8bit_constraint_flag != ptl->max_8bit_constraint_flag)
+      continue;
+    if (p.max_422chroma_constraint_flag != ptl->max_422chroma_constraint_flag)
+      continue;
+    if (p.max_420chroma_constraint_flag != ptl->max_420chroma_constraint_flag)
+      continue;
+    if (p.max_monochrome_constraint_flag != ptl->max_monochrome_constraint_flag)
+      continue;
+    if (p.intra_constraint_flag != ptl->intra_constraint_flag)
+      continue;
+    if (p.one_picture_only_constraint_flag !=
+        ptl->one_picture_only_constraint_flag)
+      continue;
+    if (p.lower_bit_rate_constraint_flag_set
+        && !ptl->lower_bit_rate_constraint_flag)
+      continue;
+
+    return p.profile;
+  }
+
+  return GST_H265_PROFILE_INVALID;
+}
+
+/**
+ * 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 (GstH265ProfileTierLevel * ptl)
 {
@@ -2657,8 +2739,11 @@
       || ptl->profile_compatibility_flag[3])
     return 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);
+
   /* TODO:
-   * - GST_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSION
    * - GST_H265_PROFILE_IDC_HIGH_THROUGHPUT
    * - GST_H265_PROFILE_IDC_SCREEN_CONTENT_CODING
    */
diff --git a/gst-libs/gst/codecparsers/gsth265parser.h b/gst-libs/gst/codecparsers/gsth265parser.h
index 45c5e54..985e1b5 100644
--- a/gst-libs/gst/codecparsers/gsth265parser.h
+++ b/gst-libs/gst/codecparsers/gsth265parser.h
@@ -46,6 +46,26 @@
  * @GST_H265_PROFILE_MAIN: Main profile (A.3.2)
  * @GST_H265_PROFILE_MAIN_10: Main 10 profile (A.3.3)
  * @GST_H265_PROFILE_MAIN_STILL_PICTURE: Main Still Picture profile (A.3.4)
+ * @GST_H265_PROFILE_MONOCHROME: Monochrome profile (A.3.4)
+ * @GST_H265_PROFILE_MONOCHROME_12: Monochrome 12-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MONOCHROME_16: Monochrome 16-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_12: Main profile 12-bits (A.3.4)
+ * @GST_H265_PROFILE_MAIN_422_10: Main 4:2:2 profile 10-bits (A.3.4)
+ * @GST_H265_PROFILE_MAIN_422_12: Main 4:2:2 profile 12-bits (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444: Main 4:4:4 profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_10: Main 4:4:4 10-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_12: Main 4:4:4 12-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_INTRA: Main Intra profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_10_INTRA: Main Intra 10-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_12_INTRA: Main Intra 12-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_422_10_INTRA: Main Intra 4:2:2 10-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_422_12_INTRA: Main Intra 4:2:2 12-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_INTRA: Main Intra 4:4:4 profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_10_INTRA: Main Intra 4:4:4 10-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_12_INTRA: Main Intra 4:4:4 12-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_16_INTRA: Main Intra 4:4:4 16-bits profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_STILL_PICTURE: Main 4:4:4 Still Picture profile (A.3.4)
+ * @GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE: Main 4:4:4 16-bits Still Picture profile (A.3.4)
  *
  * H.265 Profiles.
  *
@@ -54,7 +74,27 @@
   GST_H265_PROFILE_INVALID              = -1,
   GST_H265_PROFILE_MAIN                 = 1,
   GST_H265_PROFILE_MAIN_10              = 2,
-  GST_H265_PROFILE_MAIN_STILL_PICTURE   = 3
+  GST_H265_PROFILE_MAIN_STILL_PICTURE   = 3,
+  GST_H265_PROFILE_MONOCHROME,
+  GST_H265_PROFILE_MONOCHROME_12,
+  GST_H265_PROFILE_MONOCHROME_16,
+  GST_H265_PROFILE_MAIN_12,
+  GST_H265_PROFILE_MAIN_422_10,
+  GST_H265_PROFILE_MAIN_422_12,
+  GST_H265_PROFILE_MAIN_444,
+  GST_H265_PROFILE_MAIN_444_10,
+  GST_H265_PROFILE_MAIN_444_12,
+  GST_H265_PROFILE_MAIN_INTRA,
+  GST_H265_PROFILE_MAIN_10_INTRA,
+  GST_H265_PROFILE_MAIN_12_INTRA,
+  GST_H265_PROFILE_MAIN_422_10_INTRA,
+  GST_H265_PROFILE_MAIN_422_12_INTRA,
+  GST_H265_PROFILE_MAIN_444_INTRA,
+  GST_H265_PROFILE_MAIN_444_10_INTRA,
+  GST_H265_PROFILE_MAIN_444_12_INTRA,
+  GST_H265_PROFILE_MAIN_444_16_INTRA,
+  GST_H265_PROFILE_MAIN_444_STILL_PICTURE,
+  GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE,
 } GstH265Profile;
 
 /**
diff --git a/gst/videoparsers/gsth265parse.c b/gst/videoparsers/gsth265parse.c
index 9722d7e..46de66f 100644
--- a/gst/videoparsers/gsth265parse.c
+++ b/gst/videoparsers/gsth265parse.c
@@ -1234,6 +1234,46 @@
       return "main-10";
     case GST_H265_PROFILE_MAIN_STILL_PICTURE:
       return "main-still-picture";
+    case GST_H265_PROFILE_MONOCHROME:
+      return "monochrome";
+    case GST_H265_PROFILE_MONOCHROME_12:
+      return "monochrome-12";
+    case GST_H265_PROFILE_MONOCHROME_16:
+      return "monochrome-16";
+    case GST_H265_PROFILE_MAIN_12:
+      return "main-12";
+    case GST_H265_PROFILE_MAIN_422_10:
+      return "main-422-10";
+    case GST_H265_PROFILE_MAIN_422_12:
+      return "main-422-12";
+    case GST_H265_PROFILE_MAIN_444:
+      return "main-444";
+    case GST_H265_PROFILE_MAIN_444_10:
+      return "main-444-10";
+    case GST_H265_PROFILE_MAIN_444_12:
+      return "main-444-12";
+    case GST_H265_PROFILE_MAIN_INTRA:
+      return "main-intra";
+    case GST_H265_PROFILE_MAIN_10_INTRA:
+      return "main-10-intra";
+    case GST_H265_PROFILE_MAIN_12_INTRA:
+      return "main-12-intra";
+    case GST_H265_PROFILE_MAIN_422_10_INTRA:
+      return "main-422-10-intra";
+    case GST_H265_PROFILE_MAIN_422_12_INTRA:
+      return "main-422-12-intra";
+    case GST_H265_PROFILE_MAIN_444_INTRA:
+      return "main-444-intra";
+    case GST_H265_PROFILE_MAIN_444_10_INTRA:
+      return "main-444-10-intra";
+    case GST_H265_PROFILE_MAIN_444_12_INTRA:
+      return "main-444-12-intra";
+    case GST_H265_PROFILE_MAIN_444_16_INTRA:
+      return "main-444-16-intra";
+    case GST_H265_PROFILE_MAIN_444_STILL_PICTURE:
+      return "main-444-still-picture";
+    case GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE:
+      return "main-444-16-still-picture";
     default:
       break;
   }
diff --git a/tests/check/libs/h265parser.c b/tests/check/libs/h265parser.c
index 0a58e76..0a7a5e1 100644
--- a/tests/check/libs/h265parser.c
+++ b/tests/check/libs/h265parser.c
@@ -70,6 +70,153 @@
 
 GST_END_TEST;
 
+static void
+set_format_range_fields (GstH265ProfileTierLevel * ptl,
+    guint8 max_12bit_constraint_flag,
+    guint8 max_10bit_constraint_flag,
+    guint8 max_8bit_constraint_flag,
+    guint8 max_422chroma_constraint_flag,
+    guint8 max_420chroma_constraint_flag,
+    guint8 max_monochrome_constraint_flag,
+    guint8 intra_constraint_flag,
+    guint8 one_picture_only_constraint_flag,
+    guint8 lower_bit_rate_constraint_flag)
+{
+  ptl->max_12bit_constraint_flag = max_12bit_constraint_flag;
+  ptl->max_10bit_constraint_flag = max_10bit_constraint_flag;
+  ptl->max_8bit_constraint_flag = max_8bit_constraint_flag;
+  ptl->max_422chroma_constraint_flag = max_422chroma_constraint_flag;
+  ptl->max_420chroma_constraint_flag = max_420chroma_constraint_flag;
+  ptl->max_monochrome_constraint_flag = max_monochrome_constraint_flag;
+  ptl->intra_constraint_flag = intra_constraint_flag;
+  ptl->one_picture_only_constraint_flag = one_picture_only_constraint_flag;
+  ptl->lower_bit_rate_constraint_flag = lower_bit_rate_constraint_flag;
+}
+
+GST_START_TEST (test_h265_format_range_profiles_exact_match)
+{
+  /* Test all the combinations from Table A.2 */
+  GstH265ProfileTierLevel ptl;
+
+  memset (&ptl, 0, sizeof (ptl));
+  ptl.profile_idc = 4;
+
+  set_format_range_fields (&ptl, 1, 1, 1, 1, 1, 1, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MONOCHROME);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 1, 1, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MONOCHROME_12);
+
+  set_format_range_fields (&ptl, 0, 0, 0, 1, 1, 1, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MONOCHROME_16);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 1, 0, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_12);
+
+  set_format_range_fields (&ptl, 1, 1, 0, 1, 0, 0, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_422_10);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 0, 0, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_422_12);
+
+  set_format_range_fields (&ptl, 1, 1, 1, 0, 0, 0, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444);
+
+  set_format_range_fields (&ptl, 1, 1, 0, 0, 0, 0, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_10);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 0, 0, 0, 0, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_12);
+
+  set_format_range_fields (&ptl, 1, 1, 1, 1, 1, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_INTRA);
+  set_format_range_fields (&ptl, 1, 1, 1, 1, 1, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_INTRA);
+
+  set_format_range_fields (&ptl, 1, 1, 0, 1, 1, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_10_INTRA);
+  set_format_range_fields (&ptl, 1, 1, 0, 1, 1, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_10_INTRA);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 1, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_12_INTRA);
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 1, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_12_INTRA);
+
+  set_format_range_fields (&ptl, 1, 1, 0, 1, 0, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_422_10_INTRA);
+  set_format_range_fields (&ptl, 1, 1, 0, 1, 0, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_422_10_INTRA);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 0, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_422_12_INTRA);
+  set_format_range_fields (&ptl, 1, 0, 0, 1, 0, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_422_12_INTRA);
+
+  set_format_range_fields (&ptl, 1, 1, 1, 0, 0, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_INTRA);
+  set_format_range_fields (&ptl, 1, 1, 1, 0, 0, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_INTRA);
+
+  set_format_range_fields (&ptl, 1, 1, 0, 0, 0, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_10_INTRA);
+  set_format_range_fields (&ptl, 1, 1, 0, 0, 0, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_10_INTRA);
+
+  set_format_range_fields (&ptl, 1, 0, 0, 0, 0, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_12_INTRA);
+  set_format_range_fields (&ptl, 1, 0, 0, 0, 0, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_12_INTRA);
+
+  set_format_range_fields (&ptl, 0, 0, 0, 0, 0, 0, 1, 0, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_16_INTRA);
+  set_format_range_fields (&ptl, 0, 0, 0, 0, 0, 0, 1, 0, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_16_INTRA);
+
+  set_format_range_fields (&ptl, 1, 1, 1, 0, 0, 0, 1, 1, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_STILL_PICTURE);
+  set_format_range_fields (&ptl, 1, 1, 1, 0, 0, 0, 1, 1, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_STILL_PICTURE);
+
+  set_format_range_fields (&ptl, 0, 0, 0, 0, 0, 0, 1, 1, 0);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE);
+  set_format_range_fields (&ptl, 0, 0, 0, 0, 0, 0, 1, 1, 1);
+  g_assert_cmpuint (gst_h265_profile_tier_level_get_profile (&ptl), ==,
+      GST_H265_PROFILE_MAIN_444_16_STILL_PICTURE);
+}
+
+GST_END_TEST;
+
 static Suite *
 h265parser_suite (void)
 {
@@ -80,6 +227,7 @@
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_h265_base_profiles);
   tcase_add_test (tc_chain, test_h265_base_profiles_compat);
+  tcase_add_test (tc_chain, test_h265_format_range_profiles_exact_match);
 
   return s;
 }