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: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7405>
This commit is contained in:
Matthew Waters 2024-08-23 20:01:32 +10:00 committed by GStreamer Marge Bot
parent e9ab880e66
commit 4802ad8eb6
3 changed files with 58 additions and 6 deletions

View file

@ -18730,6 +18730,18 @@
"readable": true, "readable": true,
"type": "gint", "type": "gint",
"writable": true "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" "rank": "none"

View file

@ -109,9 +109,11 @@ enum
{ {
PROP_0, PROP_0,
PROP_COMMON_TS_OFFSET, PROP_COMMON_TS_OFFSET,
PROP_FORWARD_UNKNOWN_SSRC,
}; };
#define DEFAULT_COMMON_TS_OFFSET -1 #define DEFAULT_COMMON_TS_OFFSET -1
#define DEFAULT_FORWARD_UNKNOWN_SSRC FALSE
struct _GstRtpFunnelClass struct _GstRtpFunnelClass
{ {
@ -136,6 +138,7 @@ struct _GstRtpFunnel
/* properties */ /* properties */
gint common_ts_offset; gint common_ts_offset;
gboolean forward_unknown_ssrcs;
}; };
#define RTP_CAPS "application/x-rtp" #define RTP_CAPS "application/x-rtp"
@ -529,9 +532,10 @@ gst_rtp_funnel_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
GstPad *fpad; GstPad *fpad;
guint ssrc; guint ssrc;
if (s && gst_structure_get_uint (s, "ssrc", &ssrc)) { if (s && gst_structure_get_uint (s, "ssrc", &ssrc)) {
handled = TRUE; gboolean forward_unknown = FALSE;
GST_OBJECT_LOCK (funnel); GST_OBJECT_LOCK (funnel);
forward_unknown = funnel->forward_unknown_ssrcs;
fpad = g_hash_table_lookup (funnel->ssrc_to_pad, GUINT_TO_POINTER (ssrc)); fpad = g_hash_table_lookup (funnel->ssrc_to_pad, GUINT_TO_POINTER (ssrc));
if (fpad) if (fpad)
gst_object_ref (fpad); gst_object_ref (fpad);
@ -542,8 +546,10 @@ gst_rtp_funnel_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
event, fpad); event, fpad);
ret = gst_pad_push_event (fpad, event); ret = gst_pad_push_event (fpad, event);
gst_object_unref (fpad); gst_object_unref (fpad);
} else { handled = TRUE;
} else if (!forward_unknown) {
gst_event_unref (event); 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: case PROP_COMMON_TS_OFFSET:
funnel->common_ts_offset = g_value_get_int (value); funnel->common_ts_offset = g_value_get_int (value);
break; 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: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -616,6 +627,11 @@ gst_rtp_funnel_get_property (GObject * object, guint prop_id, GValue * value,
case PROP_COMMON_TS_OFFSET: case PROP_COMMON_TS_OFFSET:
g_value_set_int (value, funnel->common_ts_offset); g_value_set_int (value, funnel->common_ts_offset);
break; 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: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -706,6 +722,19 @@ gst_rtp_funnel_class_init (GstRtpFunnelClass * klass)
-1, G_MAXINT32, DEFAULT_COMMON_TS_OFFSET, -1, G_MAXINT32, DEFAULT_COMMON_TS_OFFSET,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); 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, GST_DEBUG_CATEGORY_INIT (gst_rtp_funnel_debug,
"gstrtpfunnel", 0, "funnel element"); "gstrtpfunnel", 0, "funnel element");
} }
@ -723,4 +752,5 @@ gst_rtp_funnel_init (GstRtpFunnel * funnel)
funnel->srccaps = gst_caps_new_empty_simple (RTP_CAPS); funnel->srccaps = gst_caps_new_empty_simple (RTP_CAPS);
funnel->ssrc_to_pad = g_hash_table_new (NULL, NULL); funnel->ssrc_to_pad = g_hash_table_new (NULL, NULL);
funnel->current_pad = NULL; funnel->current_pad = NULL;
funnel->forward_unknown_ssrcs = DEFAULT_FORWARD_UNKNOWN_SSRC;
} }

View file

@ -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 (h0));
fail_unless_equals_int (2, gst_harness_upstream_events_received (h1)); 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_harness_push_upstream_event (h,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new ("GstForceKeyUnit", 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 (h0));
fail_unless_equals_int (2, gst_harness_upstream_events_received (h1)); 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 */ /* no ssrc, we send to all */
gst_harness_push_upstream_event (h, gst_harness_push_upstream_event (h,
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new_empty ("GstForceKeyUnit"))); gst_structure_new_empty ("GstForceKeyUnit")));
fail_unless_equals_int (3, gst_harness_upstream_events_received (h0)); fail_unless_equals_int (4, 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 (h1));
/* remove pad 0, and send an event referencing the now dead ssrc */ /* remove pad 0, and send an event referencing the now dead ssrc */
gst_harness_teardown (h0); gst_harness_teardown (h0);
@ -76,7 +86,7 @@ GST_START_TEST (rtpfunnel_ssrc_demuxing)
gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
gst_structure_new ("GstForceKeyUnit", gst_structure_new ("GstForceKeyUnit",
"ssrc", G_TYPE_UINT, 123, NULL))); "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 (h);
gst_harness_teardown (h1); gst_harness_teardown (h1);