From 4802ad8eb6d01a9b9cdda8b50b5b8683be9461ce Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Fri, 23 Aug 2024 20:01:32 +1000 Subject: [PATCH] rtpfunnel: also fallback to pad default handling for unknown ssrcs If two (or more) rtpfunnel elements are cascaded, then only one will realistically have information on the particular ssrc that is in use for a particular input stream. As such, any key unit requests may never reach the corresponding encoder. This has been discovered by combining simulcast and BUNDLE with webrtcbin. simulcast uses one rtpfunnel, and BUNDLE uses another rtpfunnel. Part-of: --- .../docs/gst_plugins_cache.json | 12 +++++++ .../gst/rtpmanager/gstrtpfunnel.c | 34 +++++++++++++++++-- .../tests/check/elements/rtpfunnel.c | 18 +++++++--- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json index 33e7368647..ec48268304 100644 --- a/subprojects/gst-plugins-good/docs/gst_plugins_cache.json +++ b/subprojects/gst-plugins-good/docs/gst_plugins_cache.json @@ -18730,6 +18730,18 @@ "readable": true, "type": "gint", "writable": true + }, + "forward-unknown-ssrc": { + "blurb": "Whether to forward events or queries that reference unknown SSRCs", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true } }, "rank": "none" diff --git a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpfunnel.c b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpfunnel.c index d403015e14..0e33d05b24 100644 --- a/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpfunnel.c +++ b/subprojects/gst-plugins-good/gst/rtpmanager/gstrtpfunnel.c @@ -109,9 +109,11 @@ enum { PROP_0, PROP_COMMON_TS_OFFSET, + PROP_FORWARD_UNKNOWN_SSRC, }; #define DEFAULT_COMMON_TS_OFFSET -1 +#define DEFAULT_FORWARD_UNKNOWN_SSRC FALSE struct _GstRtpFunnelClass { @@ -136,6 +138,7 @@ struct _GstRtpFunnel /* properties */ gint common_ts_offset; + gboolean forward_unknown_ssrcs; }; #define RTP_CAPS "application/x-rtp" @@ -529,9 +532,10 @@ gst_rtp_funnel_src_event (GstPad * pad, GstObject * parent, GstEvent * event) GstPad *fpad; guint ssrc; if (s && gst_structure_get_uint (s, "ssrc", &ssrc)) { - handled = TRUE; + gboolean forward_unknown = FALSE; GST_OBJECT_LOCK (funnel); + forward_unknown = funnel->forward_unknown_ssrcs; fpad = g_hash_table_lookup (funnel->ssrc_to_pad, GUINT_TO_POINTER (ssrc)); if (fpad) gst_object_ref (fpad); @@ -542,8 +546,10 @@ gst_rtp_funnel_src_event (GstPad * pad, GstObject * parent, GstEvent * event) event, fpad); ret = gst_pad_push_event (fpad, event); gst_object_unref (fpad); - } else { + handled = TRUE; + } else if (!forward_unknown) { gst_event_unref (event); + handled = TRUE; } } } @@ -600,6 +606,11 @@ gst_rtp_funnel_set_property (GObject * object, guint prop_id, case PROP_COMMON_TS_OFFSET: funnel->common_ts_offset = g_value_get_int (value); break; + case PROP_FORWARD_UNKNOWN_SSRC: + GST_OBJECT_LOCK (funnel); + funnel->forward_unknown_ssrcs = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (funnel); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -616,6 +627,11 @@ gst_rtp_funnel_get_property (GObject * object, guint prop_id, GValue * value, case PROP_COMMON_TS_OFFSET: g_value_set_int (value, funnel->common_ts_offset); break; + case PROP_FORWARD_UNKNOWN_SSRC: + GST_OBJECT_LOCK (funnel); + g_value_set_boolean (value, funnel->forward_unknown_ssrcs); + GST_OBJECT_UNLOCK (funnel); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -706,6 +722,19 @@ gst_rtp_funnel_class_init (GstRtpFunnelClass * klass) -1, G_MAXINT32, DEFAULT_COMMON_TS_OFFSET, G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * rtpfunnel:forward-unknown-ssrc: + * + * Whether to forward events or queries that reference unknown SSRCs. + * + * Since: 1.26 + */ + g_object_class_install_property (gobject_class, PROP_FORWARD_UNKNOWN_SSRC, + g_param_spec_boolean ("forward-unknown-ssrc", "Forward Unknown SSRC", + "Whether to forward events or queries that reference unknown SSRCs", + DEFAULT_FORWARD_UNKNOWN_SSRC, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + GST_DEBUG_CATEGORY_INIT (gst_rtp_funnel_debug, "gstrtpfunnel", 0, "funnel element"); } @@ -723,4 +752,5 @@ gst_rtp_funnel_init (GstRtpFunnel * funnel) funnel->srccaps = gst_caps_new_empty_simple (RTP_CAPS); funnel->ssrc_to_pad = g_hash_table_new (NULL, NULL); funnel->current_pad = NULL; + funnel->forward_unknown_ssrcs = DEFAULT_FORWARD_UNKNOWN_SSRC; } diff --git a/subprojects/gst-plugins-good/tests/check/elements/rtpfunnel.c b/subprojects/gst-plugins-good/tests/check/elements/rtpfunnel.c index 676127ca18..1bb6be2a3d 100644 --- a/subprojects/gst-plugins-good/tests/check/elements/rtpfunnel.c +++ b/subprojects/gst-plugins-good/tests/check/elements/rtpfunnel.c @@ -55,7 +55,7 @@ GST_START_TEST (rtpfunnel_ssrc_demuxing) fail_unless_equals_int (2, gst_harness_upstream_events_received (h0)); fail_unless_equals_int (2, gst_harness_upstream_events_received (h1)); - /* unknown ssrc, we drop it */ + /* unknown ssrc, we drop it by default */ gst_harness_push_upstream_event (h, gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_structure_new ("GstForceKeyUnit", @@ -63,12 +63,22 @@ GST_START_TEST (rtpfunnel_ssrc_demuxing) fail_unless_equals_int (2, gst_harness_upstream_events_received (h0)); fail_unless_equals_int (2, gst_harness_upstream_events_received (h1)); + /* unknown ssrc, we forward if property says to */ + g_object_set (h->element, "forward-unknown-ssrc", TRUE, NULL); + gst_harness_push_upstream_event (h, + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", + "ssrc", G_TYPE_UINT, 666, NULL))); + fail_unless_equals_int (3, gst_harness_upstream_events_received (h0)); + fail_unless_equals_int (3, gst_harness_upstream_events_received (h1)); + g_object_set (h->element, "forward-unknown-ssrc", FALSE, NULL); + /* no ssrc, we send to all */ gst_harness_push_upstream_event (h, gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_structure_new_empty ("GstForceKeyUnit"))); - fail_unless_equals_int (3, gst_harness_upstream_events_received (h0)); - fail_unless_equals_int (3, gst_harness_upstream_events_received (h1)); + fail_unless_equals_int (4, gst_harness_upstream_events_received (h0)); + fail_unless_equals_int (4, gst_harness_upstream_events_received (h1)); /* remove pad 0, and send an event referencing the now dead ssrc */ gst_harness_teardown (h0); @@ -76,7 +86,7 @@ GST_START_TEST (rtpfunnel_ssrc_demuxing) gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_structure_new ("GstForceKeyUnit", "ssrc", G_TYPE_UINT, 123, NULL))); - fail_unless_equals_int (3, gst_harness_upstream_events_received (h1)); + fail_unless_equals_int (4, gst_harness_upstream_events_received (h1)); gst_harness_teardown (h); gst_harness_teardown (h1);