From be5fab15e00162a3cb0eb6f84e3bfdecb01c8869 Mon Sep 17 00:00:00 2001 From: Havard Graff Date: Thu, 19 Nov 2020 23:50:23 +0100 Subject: rtptwcc: add feedback-interval To allow RTCP TWCC reports to be scheduled on a timer instead of per marker-bit. Part-of: --- gst/rtpmanager/rtpsession.c | 29 ++++++++++++++++++++++++- gst/rtpmanager/rtptwcc.c | 34 ++++++++++++++++++++++++++++- gst/rtpmanager/rtptwcc.h | 3 +++ tests/check/elements/rtpsession.c | 45 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 2 deletions(-) diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c index 5c52766e0..b8429a1a3 100644 --- a/gst/rtpmanager/rtpsession.c +++ b/gst/rtpmanager/rtpsession.c @@ -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; diff --git a/gst/rtpmanager/rtptwcc.c b/gst/rtpmanager/rtptwcc.c index 8a7e879f6..d5039d4fc 100644 --- a/gst/rtpmanager/rtptwcc.c +++ b/gst/rtpmanager/rtptwcc.c @@ -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; } diff --git a/gst/rtpmanager/rtptwcc.h b/gst/rtpmanager/rtptwcc.h index 2342ef83f..bb14dbad2 100644 --- a/gst/rtpmanager/rtptwcc.h +++ b/gst/rtpmanager/rtptwcc.h @@ -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); diff --git a/tests/check/elements/rtpsession.c b/tests/check/elements/rtpsession.c index d3de55373..d87de24bf 100644 --- a/tests/check/elements/rtpsession.c +++ b/tests/check/elements/rtpsession.c @@ -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; } -- cgit v1.2.3