sdp: Handle level-asymmetry-allowed for H264 streams
authorThibault Saunier <tsaunier@igalia.com>
Fri, 3 Dec 2021 01:45:49 +0000 (01:45 +0000)
committerThibault Saunier <tsaunier@igalia.com>
Sun, 12 Dec 2021 13:59:00 +0000 (10:59 -0300)
The ["level-asymmetry-allowed"] field states that the peer wants the
profile specified in the "profile-level-id" fields but doesn't care
about the level. To express this in GStreamer caps term, we add a
"profile" field in the caps, which reuses the usual "profile" semantics
for H.264 streams and, and remove "profile-level-id" and
"level-asymmetry-allowed" fields.

["level-asymmetry-allowed"]: https://www.iana.org/assignments/media-types/video/H264

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

subprojects/gst-plugins-base/gst-libs/gst/meson.build
subprojects/gst-plugins-base/gst-libs/gst/sdp/gstsdpmessage.c
subprojects/gst-plugins-base/gst-libs/gst/sdp/meson.build
subprojects/gst-plugins-base/tests/check/libs/sdp.c

index cd3b5b0..42c0a6f 100644 (file)
@@ -3,9 +3,9 @@ subdir('fft')
 subdir('video')
 subdir('audio')
 subdir('rtp')
+subdir('pbutils')
 subdir('sdp')
 subdir('rtsp')
-subdir('pbutils')
 subdir('riff')
 subdir('app')
 subdir('allocators')
index 04c27b9..59f0c5c 100644 (file)
@@ -61,6 +61,7 @@
 #include <gio/gio.h>
 
 #include <gst/rtp/gstrtppayloads.h>
+#include <gst/pbutils/pbutils.h>
 #include "gstsdpmessage.h"
 
 #define FREE_STRING(field)              g_free (field); (field) = NULL
@@ -3540,6 +3541,35 @@ gst_sdp_media_add_rtcp_fb_attributes_from_media (const GstSDPMedia * media,
   return GST_SDP_OK;
 }
 
+static void
+gst_sdp_media_caps_adjust_h264 (GstCaps * caps)
+{
+  long int spsint;
+  guint8 sps[2];
+  const gchar *profile_level_id;
+  GstStructure *s = gst_caps_get_structure (caps, 0);
+
+  if (g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "H264") ||
+      g_strcmp0 (gst_structure_get_string (s, "level-asymmetry-allowed"), "1"))
+    return;
+
+  profile_level_id = gst_structure_get_string (s, "profile-level-id");
+  if (!profile_level_id)
+    return;
+
+  spsint = strtol (profile_level_id, NULL, 16);
+  sps[0] = spsint >> 16;
+  sps[1] = spsint >> 8;
+
+  GST_DEBUG ("'level-asymmetry-allowed' is set so we shouldn't care about "
+      "'profile-level-id' and only set a 'profile' instead");
+  gst_structure_set (s, "profile", G_TYPE_STRING,
+      gst_codec_utils_h264_get_profile (sps, 2), NULL);
+
+  gst_structure_remove_fields (s, "level-asymmetry-allowed", "profile-level-id",
+      NULL);
+}
+
 /**
  * gst_sdp_media_get_caps_from_media:
  * @media: a #GstSDPMedia
@@ -3714,6 +3744,8 @@ gst_sdp_media_get_caps_from_media (const GstSDPMedia * media, gint pt)
     }
   }
 
+  gst_sdp_media_caps_adjust_h264 (caps);
+
   /* parse rtcp-fb: field */
   gst_sdp_media_add_rtcp_fb_attributes_from_media (media, pt, caps);
 
@@ -3960,6 +3992,16 @@ gst_sdp_media_set_media_from_caps (const GstCaps * caps, GstSDPMedia * media)
     }
 
     if ((fval = gst_structure_get_string (s, fname))) {
+
+      /* "profile" is our internal representation of the notion of
+       * "level-asymmetry-allowed" with caps, convert it back to the SDP
+       * representation */
+      if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "H264")
+          && !g_strcmp0 (fname, "profile")) {
+        fname = "level-asymmetry-allowed";
+        fval = "1";
+      }
+
       g_string_append_printf (fmtp, "%s%s=%s", first ? "" : ";", fname, fval);
       first = FALSE;
     }
index 53d695a..746004f 100644 (file)
@@ -7,7 +7,7 @@ gst_sdp_headers = files([
 ])
 install_headers(gst_sdp_headers, subdir : 'gstreamer-1.0/gst/sdp/')
 
-sdp_deps = [rtp_dep, gst_dep, gio_dep]
+sdp_deps = [rtp_dep, gst_dep, gio_dep, pbutils_dep]
 gst_sdp_sources = files(['gstsdpmessage.c', 'gstmikey.c'])
 gstsdp = library('gstsdp-@0@'.format(api_version),
   gst_sdp_sources,
index d93b96f..40c3c8a 100644 (file)
@@ -47,6 +47,16 @@ static const gchar *sdp = "v=0\r\n"
     "a=sendrecv\r\n"
     "m=audio 1010 TCP 14\r\n";
 
+static const gchar *h264_sdp = "v=0\r\n"
+    "o=- 992782775729845470 2 IN IP4 127.0.0.1\r\n"
+    "s=TestH264\r\n"
+    "t=0 0\r\n"
+    "m=video 9 UDP/TLS/RTP/SAVPF 96\r\n"
+    "c=IN IP4 0.0.0.0\r\n"
+    "a=recvonly\r\n"
+    "a=rtpmap:96 H264/90000\r\n"
+    "a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n";
+
 static const gchar caps_video_string1[] =
     "application/x-unknown, media=(string)video, payload=(int)96, "
     "clock-rate=(int)90000, encoding-name=(string)MP4V-ES";
@@ -658,6 +668,33 @@ GST_START_TEST (caps_from_media_really_const)
 }
 
 GST_END_TEST
+GST_START_TEST (media_from_caps_h264_with_profile_asymmetry_allowed)
+{
+  GstSDPMessage *message;
+  glong length = -1;
+  const GstSDPMedia *result_video;
+  GstStructure *s_video;
+  GstCaps *caps_video;
+
+  gst_sdp_message_new (&message);
+  gst_sdp_message_parse_buffer ((guint8 *) h264_sdp, length, message);
+
+
+  result_video = gst_sdp_message_get_media (message, 0);
+  fail_unless (result_video != NULL);
+  caps_video = gst_sdp_media_get_caps_from_media (result_video, 96);
+
+  s_video = gst_caps_get_structure (caps_video, 0);
+  fail_if (gst_structure_has_field (s_video, "level-asymmetry-allowed"));
+  fail_if (gst_structure_has_field (s_video, "profile-level-id"));
+  fail_unless_equals_string (gst_structure_get_string (s_video, "profile"),
+      "constrained-baseline");
+
+  gst_caps_unref (caps_video);
+  gst_sdp_message_free (message);
+}
+
+GST_END_TEST
 /*
  * End of test cases
  */
@@ -681,6 +718,8 @@ sdp_suite (void)
   tcase_add_test (tc_chain, media_from_caps_rtcp_fb_pt_100);
   tcase_add_test (tc_chain, media_from_caps_rtcp_fb_pt_101);
   tcase_add_test (tc_chain, media_from_caps_extmap_pt_100);
+  tcase_add_test (tc_chain,
+      media_from_caps_h264_with_profile_asymmetry_allowed);
 
   return s;
 }