rtptwcc: add feedback-interval
authorHavard Graff <havard.graff@gmail.com>
Thu, 19 Nov 2020 22:50:23 +0000 (23:50 +0100)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 25 Aug 2021 08:36:06 +0000 (08:36 +0000)
To allow RTCP TWCC reports to be scheduled on a timer instead of per
marker-bit.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/927>

gst/rtpmanager/rtpsession.c
gst/rtpmanager/rtptwcc.c
gst/rtpmanager/rtptwcc.h
tests/check/elements/rtpsession.c

index 5c52766..b8429a1 100644 (file)
@@ -78,6 +78,7 @@ enum
 #define DEFAULT_RTP_PROFILE          GST_RTP_PROFILE_AVP
 #define DEFAULT_RTCP_REDUCED_SIZE    FALSE
 #define DEFAULT_RTCP_DISABLE_SR_TIMESTAMP FALSE
+#define DEFAULT_TWCC_FEEDBACK_INTERVAL GST_CLOCK_TIME_NONE
 
 enum
 {
@@ -103,7 +104,8 @@ enum
   PROP_STATS,
   PROP_RTP_PROFILE,
   PROP_RTCP_REDUCED_SIZE,
-  PROP_RTCP_DISABLE_SR_TIMESTAMP
+  PROP_RTCP_DISABLE_SR_TIMESTAMP,
+  PROP_TWCC_FEEDBACK_INTERVAL,
 };
 
 /* update average packet size */
@@ -629,6 +631,23 @@ rtp_session_class_init (RTPSessionClass * klass)
           DEFAULT_RTCP_DISABLE_SR_TIMESTAMP,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  /**
+   * RTPSession::twcc-feedback-interval:
+   *
+   * The interval to send TWCC reports on.
+   * This overrides the default behavior of sending reports
+   * based on marker-bits.
+   *
+   * Since: 1.20
+   */
+  g_object_class_install_property (gobject_class,
+      PROP_TWCC_FEEDBACK_INTERVAL,
+      g_param_spec_uint64 ("twcc-feedback-interval",
+          "TWCC Feedback Interval",
+          "The interval to send TWCC reports on",
+          0, G_MAXUINT64, DEFAULT_TWCC_FEEDBACK_INTERVAL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   klass->get_source_by_ssrc =
       GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
   klass->send_rtcp = GST_DEBUG_FUNCPTR (rtp_session_send_rtcp);
@@ -907,6 +926,10 @@ rtp_session_set_property (GObject * object, guint prop_id,
     case PROP_RTCP_DISABLE_SR_TIMESTAMP:
       sess->timestamp_sender_reports = !g_value_get_boolean (value);
       break;
+    case PROP_TWCC_FEEDBACK_INTERVAL:
+      rtp_twcc_manager_set_feedback_interval (sess->twcc,
+          g_value_get_uint64 (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -989,6 +1012,10 @@ rtp_session_get_property (GObject * object, guint prop_id,
     case PROP_RTCP_DISABLE_SR_TIMESTAMP:
       g_value_set_boolean (value, !sess->timestamp_sender_reports);
       break;
+    case PROP_TWCC_FEEDBACK_INTERVAL:
+      g_value_set_uint64 (value,
+          rtp_twcc_manager_get_feedback_interval (sess->twcc));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index 8a7e879..d5039d4 100644 (file)
@@ -87,6 +87,9 @@ struct _RTPTWCCManager
   gboolean first_fci_parse;
   guint16 expected_parsed_seqnum;
   guint8 expected_parsed_fb_pkt_count;
+
+  GstClockTime next_feedback_send_time;
+  GstClockTime feedback_interval;
 };
 
 G_DEFINE_TYPE (RTPTWCCManager, rtp_twcc_manager, G_TYPE_OBJECT);
@@ -105,6 +108,9 @@ rtp_twcc_manager_init (RTPTWCCManager * twcc)
   twcc->recv_sender_ssrc = -1;
 
   twcc->first_fci_parse = TRUE;
+
+  twcc->feedback_interval = GST_CLOCK_TIME_NONE;
+  twcc->next_feedback_send_time = GST_CLOCK_TIME_NONE;
 }
 
 static void
@@ -157,6 +163,19 @@ rtp_twcc_manager_set_mtu (RTPTWCCManager * twcc, guint mtu)
   twcc->max_packets_per_rtcp = ((twcc->mtu - 32) * 7) / (2 + 14);
 }
 
+void
+rtp_twcc_manager_set_feedback_interval (RTPTWCCManager * twcc,
+    GstClockTime feedback_interval)
+{
+  twcc->feedback_interval = feedback_interval;
+}
+
+GstClockTime
+rtp_twcc_manager_get_feedback_interval (RTPTWCCManager * twcc)
+{
+  return twcc->feedback_interval;
+}
+
 static gint
 _twcc_seqnum_sort (gconstpointer a, gconstpointer b)
 {
@@ -609,7 +628,20 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc,
   GST_LOG ("Receive: twcc-seqnum: %u, marker: %d, ts: %" GST_TIME_FORMAT,
       seqnum, pinfo->marker, GST_TIME_ARGS (pinfo->running_time));
 
-  if (pinfo->marker || _many_packets_some_lost (twcc, seqnum)) {
+  /* are we sending on an interval, or based on marker bit */
+  if (GST_CLOCK_TIME_IS_VALID (twcc->feedback_interval)) {
+    if (!GST_CLOCK_TIME_IS_VALID (twcc->next_feedback_send_time))
+      twcc->next_feedback_send_time =
+          pinfo->running_time + twcc->feedback_interval;
+
+    if (pinfo->running_time >= twcc->next_feedback_send_time) {
+      rtp_twcc_manager_create_feedback (twcc);
+      send_feedback = TRUE;
+
+      while (pinfo->running_time >= twcc->next_feedback_send_time)
+        twcc->next_feedback_send_time += twcc->feedback_interval;
+    }
+  } else if (pinfo->marker || _many_packets_some_lost (twcc, seqnum)) {
     rtp_twcc_manager_create_feedback (twcc);
     send_feedback = TRUE;
   }
index 2342ef8..bb14dba 100644 (file)
@@ -54,6 +54,9 @@ struct _RTPTWCCPacket
 RTPTWCCManager * rtp_twcc_manager_new (guint mtu);
 
 void rtp_twcc_manager_set_mtu (RTPTWCCManager * twcc, guint mtu);
+void rtp_twcc_manager_set_feedback_interval (RTPTWCCManager * twcc,
+    GstClockTime feedback_interval);
+GstClockTime rtp_twcc_manager_get_feedback_interval (RTPTWCCManager * twcc);
 
 gboolean rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc,
     guint16 seqnum, RTPPacketInfo * pinfo);
index d3de553..d87de24 100644 (file)
@@ -3685,6 +3685,48 @@ GST_START_TEST (test_twcc_send_and_recv)
 
 GST_END_TEST;
 
+typedef struct
+{
+  GstClockTime interval;
+  guint num_packets;
+  GstClockTime ts_delta;
+  guint num_feedback;
+} TWCCFeedbackIntervalCtx;
+
+static TWCCFeedbackIntervalCtx test_twcc_feedback_interval_ctx[] = {
+  {50 * GST_MSECOND, 21, 10 * GST_MSECOND, 4},
+  {50 * GST_MSECOND, 16, 7 * GST_MSECOND, 2},
+  {50 * GST_MSECOND, 16, 66 * GST_MSECOND, 15},
+  {50 * GST_MSECOND, 15, 33 * GST_MSECOND, 9},
+};
+
+GST_START_TEST (test_twcc_feedback_interval)
+{
+  SessionHarness *h = session_harness_new ();
+  GstBuffer *buf;
+  TWCCFeedbackIntervalCtx *ctx = &test_twcc_feedback_interval_ctx[__i__];
+
+  session_harness_set_twcc_recv_ext_id (h, TEST_TWCC_EXT_ID);
+  g_object_set (h->internal_session, "twcc-feedback-interval", ctx->interval,
+      NULL);
+
+  for (guint i = 0; i < ctx->num_packets; i++) {
+    GstClockTime ts = i * ctx->ts_delta;
+    gst_test_clock_set_time ((h->testclock), ts);
+    fail_unless_equals_int (GST_FLOW_OK,
+        session_harness_recv_rtp (h, generate_twcc_recv_buffer (i, ts, FALSE)));
+  }
+
+  for (guint i = 0; i < ctx->num_feedback; i++) {
+    buf = session_harness_produce_twcc (h);
+    gst_buffer_unref (buf);
+  }
+
+  session_harness_free (h);
+}
+
+GST_END_TEST;
+
 static Suite *
 rtpsession_suite (void)
 {
@@ -3749,6 +3791,9 @@ rtpsession_suite (void)
   tcase_add_test (tc_chain, test_twcc_recv_rtcp_reordered);
   tcase_add_test (tc_chain, test_twcc_no_exthdr_in_buffer);
   tcase_add_test (tc_chain, test_twcc_send_and_recv);
+  tcase_add_loop_test (tc_chain, test_twcc_feedback_interval, 0,
+      G_N_ELEMENTS (test_twcc_feedback_interval_ctx));
+
 
   return s;
 }