rtpbasepayload: add property for embedding twcc sequencenumbers
authorHåvard Graff <havard.graff@gmail.com>
Fri, 14 Feb 2020 09:40:59 +0000 (09:40 +0000)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Fri, 14 Feb 2020 09:40:59 +0000 (09:40 +0000)
By setting the extension-ID for TWCC (Transport Wide Congestion Control),
the payloader will embed sequencenumbers as a RTP header-extension
according to https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#section-2

The negotiation of this being enabled with downstream elements
is done with caps reflecting the way this is communicated using SDP.

gst-libs/gst/rtp/gstrtpbasepayload.c
tests/check/libs/rtpbasepayload.c

index 4127546..8167abc 100644 (file)
@@ -47,6 +47,7 @@ struct _GstRTPBasePayloadPrivate
 
   gboolean source_info;
   GstBuffer *input_meta_buffer;
+  guint8 twcc_ext_id;
 
   guint64 base_offset;
   gint64 base_rtime;
@@ -92,6 +93,7 @@ enum
 #define DEFAULT_RUNNING_TIME            GST_CLOCK_TIME_NONE
 #define DEFAULT_SOURCE_INFO             FALSE
 #define DEFAULT_ONVIF_NO_RATE_CONTROL   FALSE
+#define DEFAULT_TWCC_EXT_ID             0
 
 enum
 {
@@ -110,6 +112,7 @@ enum
   PROP_STATS,
   PROP_SOURCE_INFO,
   PROP_ONVIF_NO_RATE_CONTROL,
+  PROP_TWCC_EXT_ID,
   PROP_LAST
 };
 
@@ -337,6 +340,28 @@ gst_rtp_base_payload_class_init (GstRTPBasePayloadClass * klass)
           DEFAULT_ONVIF_NO_RATE_CONTROL,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * GstRTPBasePayload:twcc-ext-id:
+   *
+   * The RTP header-extension ID used for tagging buffers with Transport-Wide
+   * Congestion Control sequence-numbers.
+   * 
+   * To use this across multiple bundled streams (transport wide), the
+   * GstRTPFunnel can mux TWCC sequence-numbers together.
+   * 
+   * This is experimental, as it is still a draft and not yet a standard.
+   *
+   * Since: 1.18
+   */
+  g_object_class_install_property (gobject_class, PROP_TWCC_EXT_ID,
+      g_param_spec_uint ("twcc-ext-id",
+          "Transport-wide Congestion Control Extension ID (experimental)",
+          "The RTP header-extension ID to use for tagging buffers with "
+          "Transport-wide Congestion Control sequencenumbers (0 = disable)",
+          0, 15, DEFAULT_TWCC_EXT_ID,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+
   gstelement_class->change_state = gst_rtp_base_payload_change_state;
 
   klass->get_caps = gst_rtp_base_payload_getcaps_default;
@@ -1115,6 +1140,16 @@ gst_rtp_base_payload_negotiate (GstRTPBasePayload * payload)
 
   update_max_ptime (payload);
 
+
+  if (payload->priv->twcc_ext_id > 0) {
+    /* TODO: put this as a separate utility-function for RTP extensions */
+    gchar *name = g_strdup_printf ("extmap-%u", payload->priv->twcc_ext_id);
+    gst_caps_set_simple (srccaps, name, G_TYPE_STRING,
+        "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
+        NULL);
+    g_free (name);
+  }
+
   res = gst_pad_set_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload), srccaps);
   gst_caps_unref (srccaps);
   gst_caps_unref (templ);
@@ -1162,6 +1197,7 @@ typedef struct
   GstClockTime pts;
   guint64 offset;
   guint32 rtptime;
+  guint8 twcc_ext_id;
 } HeaderData;
 
 static gboolean
@@ -1180,6 +1216,16 @@ find_timestamp (GstBuffer ** buffer, guint idx, gpointer user_data)
     return TRUE;
 }
 
+static void
+_set_twcc_seq (GstRTPBuffer * rtp, guint16 seq, guint8 ext_id)
+{
+  guint16 data;
+  if (ext_id == 0 || ext_id > 14)
+    return;
+  GST_WRITE_UINT16_BE (&data, seq);
+  gst_rtp_buffer_add_extension_onebyte_header (rtp, ext_id, &data, 2);
+}
+
 static gboolean
 set_headers (GstBuffer ** buffer, guint idx, gpointer user_data)
 {
@@ -1193,6 +1239,7 @@ set_headers (GstBuffer ** buffer, guint idx, gpointer user_data)
   gst_rtp_buffer_set_payload_type (&rtp, data->pt);
   gst_rtp_buffer_set_seq (&rtp, data->seqnum);
   gst_rtp_buffer_set_timestamp (&rtp, data->rtptime);
+  _set_twcc_seq (&rtp, data->seqnum, data->twcc_ext_id);
   gst_rtp_buffer_unmap (&rtp);
 
   /* increment the seqnum for each buffer */
@@ -1249,6 +1296,7 @@ gst_rtp_base_payload_prepare_push (GstRTPBasePayload * payload,
   data.seqnum = payload->seqnum;
   data.ssrc = payload->current_ssrc;
   data.pt = payload->pt;
+  data.twcc_ext_id = priv->twcc_ext_id;
 
   /* find the first buffer with a timestamp */
   if (is_list) {
@@ -1566,6 +1614,9 @@ gst_rtp_base_payload_set_property (GObject * object, guint prop_id,
     case PROP_ONVIF_NO_RATE_CONTROL:
       priv->onvif_no_rate_control = g_value_get_boolean (value);
       break;
+    case PROP_TWCC_EXT_ID:
+      priv->twcc_ext_id = g_value_get_uint (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1636,6 +1687,9 @@ gst_rtp_base_payload_get_property (GObject * object, guint prop_id,
     case PROP_ONVIF_NO_RATE_CONTROL:
       g_value_set_boolean (value, priv->onvif_no_rate_control);
       break;
+    case PROP_TWCC_EXT_ID:
+      g_value_set_uint (value, priv->twcc_ext_id);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index 8dcf6a7..5319830 100644 (file)
@@ -1969,6 +1969,53 @@ GST_START_TEST (rtp_base_payload_segment_time)
 
 GST_END_TEST;
 
+#define TWCC_EXTMAP_STR "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01"
+
+GST_START_TEST (rtp_base_payload_property_twcc_ext_id_test)
+{
+  GstHarness *h;
+  GstRtpDummyPay *pay;
+  GstBuffer *buf;
+  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+  guint8 ext_id = 10;
+  gpointer data;
+  guint size;
+  guint16 seqnum, twcc_seqnum;
+  GstCaps *caps, *expected_caps;
+
+  pay = rtp_dummy_pay_new ();
+  g_object_set (pay, "twcc-ext-id", ext_id, NULL);
+
+  h = gst_harness_new_with_element (GST_ELEMENT_CAST (pay), "sink", "src");
+  gst_harness_set_src_caps_str (h, "application/x-rtp");
+
+  /* verify the presence of the twcc-seqnum */
+  buf = gst_harness_push_and_pull (h, gst_buffer_new ());
+  gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp);
+  fail_unless (gst_rtp_buffer_get_extension_onebyte_header (&rtp, ext_id,
+          0, &data, &size));
+  fail_unless_equals_int (2, size);
+  twcc_seqnum = GST_READ_UINT16_BE (data);
+  seqnum = gst_rtp_buffer_get_seq (&rtp);
+  fail_unless_equals_int (twcc_seqnum, seqnum);
+  gst_rtp_buffer_unmap (&rtp);
+  gst_buffer_unref (buf);
+
+  /* verify the presence of the twcc in caps */
+  caps = gst_pad_get_current_caps (GST_PAD_PEER (h->sinkpad));
+  expected_caps = gst_caps_from_string ("application/x-rtp, "
+      "extmap-10=" TWCC_EXTMAP_STR "");
+  fail_unless (gst_caps_is_subset (caps, expected_caps));
+  gst_caps_unref (caps);
+  gst_caps_unref (expected_caps);
+
+  g_object_unref (pay);
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
+
 static Suite *
 rtp_basepayloading_suite (void)
 {
@@ -2003,6 +2050,7 @@ rtp_basepayloading_suite (void)
   tcase_add_test (tc_chain, rtp_base_payload_property_ptime_multiple_test);
   tcase_add_test (tc_chain, rtp_base_payload_property_stats_test);
   tcase_add_test (tc_chain, rtp_base_payload_property_source_info_test);
+  tcase_add_test (tc_chain, rtp_base_payload_property_twcc_ext_id_test);
 
   tcase_add_test (tc_chain, rtp_base_payload_framerate_attribute);
   tcase_add_test (tc_chain, rtp_base_payload_max_framerate_attribute);