pbutils: New AV1 caps utility functions
authorEdward Hervey <edward@centricular.com>
Tue, 18 Apr 2023 09:34:54 +0000 (11:34 +0200)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Fri, 20 Dec 2024 14:19:00 +0000 (14:19 +0000)
Makes it easy to convert between caps and codec_data

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

girs/GstPbutils-1.0.gir
subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c
subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h

index d6b8c2f4ac1a0cdb4b13c676f313c4a42bcf4d3e..057276f32ad63acd98383457246b5fd165cb927a 100644 (file)
@@ -3166,6 +3166,35 @@ rate.</doc>
         </parameter>
       </parameters>
     </function>
+    <function name="codec_utils_av1_create_av1c_from_caps" c:identifier="gst_codec_utils_av1_create_av1c_from_caps" version="1.26">
+      <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Creates the corresponding AV1 Codec Configuration Record</doc>
+      <source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
+      <return-value transfer-ownership="full">
+        <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">The AV1 Codec Configuration Record, or NULL if
+there was an error.</doc>
+        <type name="Gst.Buffer" c:type="GstBuffer*"/>
+      </return-value>
+      <parameters>
+        <parameter name="caps" transfer-ownership="none">
+          <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">a video/x-av1 #GstCaps</doc>
+          <type name="Gst.Caps" c:type="GstCaps*"/>
+        </parameter>
+      </parameters>
+    </function>
+    <function name="codec_utils_av1_create_caps_from_av1c" c:identifier="gst_codec_utils_av1_create_caps_from_av1c" version="1.26">
+      <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Parses the provided @av1c and returns the corresponding caps</doc>
+      <source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
+      <return-value transfer-ownership="full">
+        <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">The parsed AV1 caps, or NULL if there is an error</doc>
+        <type name="Gst.Caps" c:type="GstCaps*"/>
+      </return-value>
+      <parameters>
+        <parameter name="av1c" transfer-ownership="none">
+          <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">a #GstBuffer containing a AV1CodecConfigurationRecord</doc>
+          <type name="Gst.Buffer" c:type="GstBuffer*"/>
+        </parameter>
+      </parameters>
+    </function>
     <function name="codec_utils_av1_get_level" c:identifier="gst_codec_utils_av1_get_level" version="1.26">
       <doc xml:space="preserve" filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.c">Transform a seq_level_idx into the level string</doc>
       <source-position filename="../subprojects/gst-plugins-base/gst-libs/gst/pbutils/codec-utils.h"/>
index b7e19fe504ba8135ae30d556bac0e85350b119a4..87e65272b68e2dbd45b3c0ccab3dd981fbfaad98 100644 (file)
@@ -2417,6 +2417,231 @@ done:
   return ret;
 }
 
+/**
+ * gst_codec_utils_av1_create_caps_from_av1c:
+ * @av1c: (transfer none): a #GstBuffer containing a AV1CodecConfigurationRecord
+ *
+ * Parses the provided @av1c and returns the corresponding caps
+ *
+ * Since: 1.26
+ *
+ * Returns: (transfer full): The parsed AV1 caps, or NULL if there is an error
+ */
+GstCaps *
+gst_codec_utils_av1_create_caps_from_av1c (GstBuffer * av1c)
+{
+  GstMapInfo map;
+  GstCaps *ret = NULL;
+  const gchar *profile, *chroma_format;
+  guint bit_depth_luma = 8;
+  gint presentation_delay = -1;
+
+  g_return_val_if_fail (av1c, NULL);
+
+  if (!gst_buffer_map (av1c, &map, GST_MAP_READ))
+    return NULL;
+
+  if (map.size < 4) {
+    GST_WARNING ("av1c too small");
+    goto done;
+  }
+
+  /*
+   *  unsigned int (1) marker = 1;
+   *  unsigned int (7) version = 1;
+   */
+  if (map.data[0] != 0x81) {
+    GST_WARNING ("Wrong av1c marker/version: 0x%02x", map.data[0]);
+    goto done;
+  }
+
+  /*
+   *  unsigned int (3) seq_profile;
+   *  unsigned int (5) seq_level_idx_0;
+   */
+  switch (map.data[1] >> 5) {
+    case 0:
+      profile = "main";
+      break;
+    case 1:
+      profile = "high";
+      break;
+    case 2:
+      profile = "professional";
+      break;
+    default:
+      GST_WARNING ("Invalid seq_profile %d", map.data[1] >> 5);
+      goto done;
+  }
+
+  /* FIXME : Add level processing */
+
+  /*
+   *  unsigned int (1) seq_tier_0;
+   *  unsigned int (1) high_bitdepth;
+   *  unsigned int (1) twelve_bit;
+   *  unsigned int (1) monochrome;
+   *  unsigned int (1) chroma_subsampling_x;
+   *  unsigned int (1) chroma_subsampling_y;
+   *  unsigned int (2) chroma_sample_position;
+   */
+  if ((map.data[2] & 0x60) == 0x60) {
+    bit_depth_luma = 12;
+  } else if ((map.data[2] & 0x60) == 0x40) {
+    bit_depth_luma = 10;
+  }
+
+  switch (map.data[2] & 0x1c) {
+    case 0x1c:
+      chroma_format = "4:0:0";
+      break;
+    case 0x0c:
+      chroma_format = "4:2:0";
+      break;
+    case 0x08:
+      chroma_format = "4:2:2";
+      break;
+    case 0x00:
+      chroma_format = "4:4:4";
+      break;
+    default:
+      GST_WARNING ("invalid chroma format values");
+      goto done;
+  }
+
+  /*
+   *  unsigned int (3) reserved = 0;
+   *
+   *  unsigned int (1) initial_presentation_delay_present;
+   *  if (initial_presentation_delay_present) {
+   *    unsigned int (4) initial_presentation_delay_minus_one;
+   *  } else {
+   *    unsigned int (4) reserved = 0;
+   *  }
+   */
+  if (map.data[3] & 0x10)
+    presentation_delay = map.data[3] & 0xf;
+
+  ret = gst_caps_new_simple ("video/x-av1", "profile", G_TYPE_STRING, profile,
+      "bit-depth-luma", G_TYPE_UINT, bit_depth_luma,
+      "chroma-format", G_TYPE_STRING, chroma_format, NULL);
+
+  if (presentation_delay != -1)
+    gst_caps_set_simple (ret, "presentation-delay", G_TYPE_INT,
+        presentation_delay, NULL);
+
+  /* FIXME : Extract more information from optional configOBU */
+
+done:
+  gst_buffer_unmap (av1c, &map);
+
+  return ret;
+}
+
+/**
+ * gst_codec_utils_av1_create_av1c_from_caps:
+ * @caps: a video/x-av1 #GstCaps
+ *
+ * Creates the corresponding AV1 Codec Configuration Record
+ *
+ * Since: 1.26
+ *
+ * Returns: (transfer full): The AV1 Codec Configuration Record, or NULL if
+ * there was an error.
+ */
+
+GstBuffer *
+gst_codec_utils_av1_create_av1c_from_caps (GstCaps * caps)
+{
+  gint presentation_delay = -1;
+  GstBuffer *av1_codec_data = NULL;
+  GstStructure *structure;
+  GstMapInfo map;
+  const gchar *tmp;
+  guint tmp2;
+
+  g_return_val_if_fail (caps, NULL);
+
+  structure = gst_caps_get_structure (caps, 0);
+  if (!structure || !gst_structure_has_name (structure, "video/x-av1")) {
+    GST_WARNING ("Caps provided are not video/x-av1");
+    return NULL;
+  }
+
+  gst_structure_get_int (structure, "presentation-delay", &presentation_delay);
+
+  av1_codec_data = gst_buffer_new_allocate (NULL, 4, NULL);
+  gst_buffer_map (av1_codec_data, &map, GST_MAP_WRITE);
+
+  /*
+   *  unsigned int (1) marker = 1;
+   *  unsigned int (7) version = 1;
+   *  unsigned int (3) seq_profile;
+   *  unsigned int (5) seq_level_idx_0;
+   *  unsigned int (1) seq_tier_0;
+   *  unsigned int (1) high_bitdepth;
+   *  unsigned int (1) twelve_bit;
+   *  unsigned int (1) monochrome;
+   *  unsigned int (1) chroma_subsampling_x;
+   *  unsigned int (1) chroma_subsampling_y;
+   *  unsigned int (2) chroma_sample_position;
+   *  unsigned int (3) reserved = 0;
+   *
+   *  unsigned int (1) initial_presentation_delay_present;
+   *  if (initial_presentation_delay_present) {
+   *    unsigned int (4) initial_presentation_delay_minus_one;
+   *  } else {
+   *    unsigned int (4) reserved = 0;
+   *  }
+   */
+
+  map.data[0] = 0x81;
+  map.data[1] = 0x00;
+  if ((tmp = gst_structure_get_string (structure, "profile"))) {
+    if (strcmp (tmp, "main") == 0)
+      map.data[1] |= (0 << 5);
+    if (strcmp (tmp, "high") == 0)
+      map.data[1] |= (1 << 5);
+    if (strcmp (tmp, "professional") == 0)
+      map.data[1] |= (2 << 5);
+  }
+  /* FIXME: level set to 1 */
+  map.data[1] |= 0x01;
+  /* FIXME: tier set to 0 */
+
+  if (gst_structure_get_uint (structure, "bit-depth-luma", &tmp2)) {
+    if (tmp2 == 10) {
+      map.data[2] |= 0x40;
+    } else if (tmp2 == 12) {
+      map.data[2] |= 0x60;
+    }
+  }
+
+  /* Assume 4:2:0 if nothing else is given */
+  map.data[2] |= 0x0C;
+  if ((tmp = gst_structure_get_string (structure, "chroma-format"))) {
+    if (strcmp (tmp, "4:0:0") == 0)
+      map.data[2] |= 0x1C;
+    if (strcmp (tmp, "4:2:0") == 0)
+      map.data[2] |= 0x0C;
+    if (strcmp (tmp, "4:2:2") == 0)
+      map.data[2] |= 0x08;
+    if (strcmp (tmp, "4:4:4") == 0)
+      map.data[2] |= 0x00;
+  }
+
+  /* FIXME: keep chroma-site unknown */
+
+  if (presentation_delay != -1) {
+    map.data[3] = 0x10 | (MAX (0xF, presentation_delay) & 0xF);
+  }
+
+  gst_buffer_unmap (av1_codec_data, &map);
+
+  return av1_codec_data;
+}
+
+
 static gboolean
 h264_caps_structure_get_profile_flags_level (GstStructure * caps_st,
     guint8 * profile, guint8 * flags, guint8 * level)
index 87127e43a693d3f7cfb51b6fd02ed2abfb164cd4..076b45f1870496f2a8a078b427c2f142970eeb89 100644 (file)
@@ -161,6 +161,13 @@ gboolean  gst_codec_utils_opus_parse_header (GstBuffer * header,
                                              guint16   * pre_skip,
                                              gint16    * output_gain);
 
+/* AV1 */
+GST_PBUTILS_API
+GstCaps * gst_codec_utils_av1_create_caps_from_av1c (GstBuffer *av1c);
+
+GST_PBUTILS_API
+GstBuffer * gst_codec_utils_av1_create_av1c_from_caps (GstCaps *caps);
+
 /* General */
 GST_PBUTILS_API
 gchar * gst_codec_utils_caps_get_mime_codec (GstCaps * caps);