diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index 519ead4fbd..00d4861048 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -236665,12 +236665,13 @@ "caps": "application/x-rtp:\n", "direction": "sink", "presence": "request", - "type": "GstWebRTCBinPad" + "type": "GstWebRTCBinSinkPad" }, "src_%%u": { "caps": "application/x-rtp:\n", "direction": "src", - "presence": "sometimes" + "presence": "sometimes", + "type": "GstWebRTCBinSrcPad" } }, "properties": { @@ -237128,6 +237129,60 @@ "writable": false } } + }, + "GstWebRTCBinSinkPad": { + "hierarchy": [ + "GstWebRTCBinSinkPad", + "GstWebRTCBinPad", + "GstGhostPad", + "GstProxyPad", + "GstPad", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "kind": "object", + "properties": { + "msid": { + "blurb": "Local MediaStream ID to use for this pad (NULL = unset)", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + } + } + }, + "GstWebRTCBinSrcPad": { + "hierarchy": [ + "GstWebRTCBinSrcPad", + "GstWebRTCBinPad", + "GstGhostPad", + "GstProxyPad", + "GstPad", + "GstObject", + "GInitiallyUnowned", + "GObject" + ], + "kind": "object", + "properties": { + "msid": { + "blurb": "Remote MediaStream ID in use for this pad (NULL = not advertised)", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": false + } + } } }, "package": "GStreamer Bad Plug-ins", diff --git a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c index 2401fd4762..5377aa3c6c 100644 --- a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c +++ b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c @@ -254,13 +254,9 @@ gst_webrtc_bin_pad_finalize (GObject * object) { GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object); - if (pad->trans) - gst_object_unref (pad->trans); - pad->trans = NULL; - - if (pad->received_caps) - gst_caps_unref (pad->received_caps); - pad->received_caps = NULL; + gst_clear_object (&pad->trans); + gst_clear_caps (&pad->received_caps); + g_clear_pointer (&pad->msid, g_free); G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object); } @@ -457,33 +453,177 @@ gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad) } static GstWebRTCBinPad * -gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction) +gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction, + char *msid) { GstWebRTCBinPad *pad; GstPadTemplate *template; + GType pad_type; - if (direction == GST_PAD_SINK) + if (direction == GST_PAD_SINK) { template = gst_static_pad_template_get (&sink_template); - else if (direction == GST_PAD_SRC) + pad_type = GST_TYPE_WEBRTC_BIN_SINK_PAD; + } else if (direction == GST_PAD_SRC) { template = gst_static_pad_template_get (&src_template); - else + pad_type = GST_TYPE_WEBRTC_BIN_SRC_PAD; + } else { g_assert_not_reached (); + } pad = - g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction", + g_object_new (pad_type, "name", name, "direction", direction, "template", template, NULL); gst_object_unref (template); - if (direction == GST_PAD_SINK) { - gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event); - gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query); - } + pad->msid = msid; GST_DEBUG_OBJECT (pad, "new visible pad with direction %s", direction == GST_PAD_SRC ? "src" : "sink"); return pad; } +enum +{ + PROP_SINK_PAD_MSID = 1, +}; + +/** + * GstWebRTCBinSinkPad: + * + * Since: 1.22 + */ +struct _GstWebRTCBinSinkPad +{ + GstWebRTCBinPad pad; +}; + +G_DEFINE_TYPE (GstWebRTCBinSinkPad, gst_webrtc_bin_sink_pad, + GST_TYPE_WEBRTC_BIN_PAD); + +static void +gst_webrtc_bin_sink_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object); + + switch (prop_id) { + case PROP_SINK_PAD_MSID: + g_value_set_string (value, pad->msid); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_webrtc_bin_sink_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object); + + switch (prop_id) { + case PROP_SINK_PAD_MSID: + g_free (pad->msid); + pad->msid = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_webrtc_bin_sink_pad_class_init (GstWebRTCBinSinkPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->get_property = gst_webrtc_bin_sink_pad_get_property; + gobject_class->set_property = gst_webrtc_bin_sink_pad_set_property; + + /** + * GstWebRTCBinSinkPad:msid: + * + * The MediaStream Identifier to use for this pad (MediaStreamTrack). + * Fallback is the RTP SDES cname value if not provided. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, + PROP_SINK_PAD_MSID, + g_param_spec_string ("msid", "MSID", + "Local MediaStream ID to use for this pad (NULL = unset)", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_webrtc_bin_sink_pad_init (GstWebRTCBinSinkPad * pad) +{ + gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event); + gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query); +} + +enum +{ + PROP_SRC_PAD_MSID = 1, +}; + +/** + * GstWebRTCBinSrcPad: + * + * Since: 1.22 + */ +struct _GstWebRTCBinSrcPad +{ + GstWebRTCBinPad pad; +}; + +G_DEFINE_TYPE (GstWebRTCBinSrcPad, gst_webrtc_bin_src_pad, + GST_TYPE_WEBRTC_BIN_PAD); + +static void +gst_webrtc_bin_src_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object); + + switch (prop_id) { + case PROP_SRC_PAD_MSID: + g_value_set_string (value, pad->msid); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_webrtc_bin_src_pad_class_init (GstWebRTCBinSrcPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->get_property = gst_webrtc_bin_src_pad_get_property; + + /** + * GstWebRTCBinSrcPad:msid: + * + * The MediaStream Identifier the remote peer used for this pad (MediaStreamTrack). + * Will be NULL if not advertised in the remote SDP. + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, + PROP_SRC_PAD_MSID, + g_param_spec_string ("msid", "MSID", + "Remote MediaStream ID in use for this pad (NULL = not advertised)", + NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_webrtc_bin_src_pad_init (GstWebRTCBinSrcPad * pad) +{ +} + #define gst_webrtc_bin_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN, G_ADD_PRIVATE (GstWebRTCBin) @@ -2867,15 +3007,27 @@ _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data) gchar *str; GstStructure *sdes; const gchar *cname; + GstWebRTCBinPad *sink_pad; + const char *msid = NULL; g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL); /* http://www.freesoft.org/CIE/RFC/1889/24.htm */ cname = gst_structure_get_string (sdes, "cname"); + sink_pad = + _find_pad_for_transceiver (data->webrtc, GST_PAD_SINK, + GST_WEBRTC_RTP_TRANSCEIVER (data->trans)); + if (sink_pad) + msid = sink_pad->msid; + /* fallback to cname if no msid provided */ + if (!msid) + msid = cname; + /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */ + /* FIXME: the ssrc is not present in RFC8830, do we still need that? */ str = g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value), - cname, GST_OBJECT_NAME (data->trans)); + msid, GST_OBJECT_NAME (data->trans)); gst_sdp_media_add_attribute (data->media, "ssrc", str); g_free (str); @@ -2883,6 +3035,7 @@ _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data) gst_sdp_media_add_attribute (data->media, "ssrc", str); g_free (str); + gst_clear_object (&sink_pad); gst_structure_free (sdes); return TRUE; @@ -2911,10 +3064,22 @@ _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc, if (gst_structure_get_uint (s, "ssrc", &ssrc)) { gchar *str; + GstWebRTCBinPad *sink_pad; + const char *msid = NULL; + + sink_pad = + _find_pad_for_transceiver (webrtc, GST_PAD_SINK, + GST_WEBRTC_RTP_TRANSCEIVER (trans)); + if (sink_pad) + msid = sink_pad->msid; + /* fallback to cname if no msid provided */ + if (!msid) + msid = cname; /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */ + /* FIXME: the ssrc is not present in RFC8830, do we still need that? */ str = - g_strdup_printf ("%u msid:%s %s", ssrc, cname, + g_strdup_printf ("%u msid:%s %s", ssrc, msid, GST_OBJECT_NAME (trans)); gst_sdp_media_add_attribute (media, "ssrc", str); g_free (str); @@ -2922,6 +3087,8 @@ _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc, str = g_strdup_printf ("%u cname:%s", ssrc, cname); gst_sdp_media_add_attribute (media, "ssrc", str); g_free (str); + + gst_clear_object (&sink_pad); } } @@ -4697,7 +4864,7 @@ gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc, static GstWebRTCBinPad * _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction, - GstWebRTCRTPTransceiver * trans, guint serial) + GstWebRTCRTPTransceiver * trans, guint serial, char *msid) { GstWebRTCBinPad *pad; gchar *pad_name; @@ -4712,7 +4879,7 @@ _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction, pad_name = g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink", serial); - pad = gst_webrtc_bin_pad_new (pad_name, direction); + pad = gst_webrtc_bin_pad_new (pad_name, direction, msid); g_free (pad_name); pad->trans = gst_object_ref (trans); @@ -5420,12 +5587,21 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans); GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction; GstWebRTCRTPTransceiverDirection new_dir; + const GstSDPMedia *local_media, *remote_media; const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx); GstWebRTCDTLSSetup new_setup; + char *local_msid = NULL; gboolean new_rtcp_rsize; ReceiveState receive_state = RECEIVE_STATE_UNSET; int i; + local_media = + gst_sdp_message_get_media (webrtc->current_local_description->sdp, + media_idx); + remote_media = + gst_sdp_message_get_media (webrtc->current_remote_description->sdp, + media_idx); + rtp_trans->mline = media_idx; if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) { @@ -5452,17 +5628,9 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, } { - const GstSDPMedia *local_media, *remote_media; GstWebRTCRTPTransceiverDirection local_dir, remote_dir; GstWebRTCDTLSSetup local_setup, remote_setup; - local_media = - gst_sdp_message_get_media (webrtc->current_local_description->sdp, - media_idx); - remote_media = - gst_sdp_message_get_media (webrtc->current_remote_description->sdp, - media_idx); - local_setup = _get_dtls_setup_from_media (local_media); remote_setup = _get_dtls_setup_from_media (remote_media); new_setup = _get_final_setup (local_setup, remote_setup); @@ -5555,16 +5723,30 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) { GstWebRTCBinPad *pad = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans); + local_msid = _get_msid_from_media (local_media); if (pad) { GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT - " for transceiver %" GST_PTR_FORMAT, pad, trans); + " for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans, + pad->msid); + if (g_strcmp0 (pad->msid, local_msid) != 0) { + GST_DEBUG_OBJECT (webrtc, "send pad %" GST_PTR_FORMAT + " transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'" + " to \'%s\'", pad, trans, pad->msid, local_msid); + g_clear_pointer (&pad->msid, g_free); + pad->msid = local_msid; + g_object_notify (G_OBJECT (pad), "msid"); + local_msid = NULL; + } else { + g_clear_pointer (&local_msid, g_free); + } gst_object_unref (pad); } else { GST_DEBUG_OBJECT (webrtc, "creating new send pad for transceiver %" GST_PTR_FORMAT, trans); pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans, - G_MAXUINT); + G_MAXUINT, local_msid); + local_msid = NULL; _connect_input_stream (webrtc, pad); _add_pad (webrtc, pad); } @@ -5573,15 +5755,30 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) { GstWebRTCBinPad *pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans); + char *remote_msid = _get_msid_from_media (remote_media); + if (pad) { GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT - " for transceiver %" GST_PTR_FORMAT, pad, trans); + " for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans, + pad->msid); + if (g_strcmp0 (pad->msid, remote_msid) != 0) { + GST_DEBUG_OBJECT (webrtc, "receive pad %" GST_PTR_FORMAT + " transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'" + " to \'%s\'", pad, trans, pad->msid, remote_msid); + g_clear_pointer (&pad->msid, g_free); + pad->msid = remote_msid; + remote_msid = NULL; + g_object_notify (G_OBJECT (pad), "msid"); + } else { + g_clear_pointer (&remote_msid, g_free); + } gst_object_unref (pad); } else { GST_DEBUG_OBJECT (webrtc, "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans); pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, - G_MAXUINT); + G_MAXUINT, remote_msid); + remote_msid = NULL; if (!trans->stream) { TransportStream *item; @@ -5597,7 +5794,6 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, * as soon as the pad is added */ _add_pad_to_list (webrtc, pad); } - } rtp_trans->mline = media_idx; @@ -7058,7 +7254,8 @@ on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad, * or somesuch */ gst_clear_object (&pad); pad = - _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT); + _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT, + NULL); GST_TRACE_OBJECT (webrtc, "duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %" GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name); @@ -7940,7 +8137,7 @@ gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ, } } } - pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial); + pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial, NULL); pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST, @@ -8265,8 +8462,9 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) element_class->change_state = gst_webrtc_bin_change_state; gst_element_class_add_static_pad_template_with_gtype (element_class, - &sink_template, GST_TYPE_WEBRTC_BIN_PAD); - gst_element_class_add_static_pad_template (element_class, &src_template); + &sink_template, GST_TYPE_WEBRTC_BIN_SINK_PAD); + gst_element_class_add_static_pad_template_with_gtype (element_class, + &src_template, GST_TYPE_WEBRTC_BIN_SRC_PAD); gst_element_class_set_metadata (element_class, "WebRTC Bin", "Filter/Network/WebRTC", "A bin for webrtc connections", @@ -8764,6 +8962,8 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE); gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0); + gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SINK_PAD, 0); + gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SRC_PAD, 0); } static void diff --git a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.h b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.h index 9663224440..9445d9e5a4 100644 --- a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.h +++ b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.h @@ -38,6 +38,8 @@ GType gst_webrtc_bin_pad_get_type(void); typedef struct _GstWebRTCBinPad GstWebRTCBinPad; typedef struct _GstWebRTCBinPadClass GstWebRTCBinPadClass; +G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstWebRTCBinPad, gst_object_unref); + struct _GstWebRTCBinPad { GstGhostPad parent; @@ -46,6 +48,7 @@ struct _GstWebRTCBinPad gulong block_id; GstCaps *received_caps; + char *msid; }; struct _GstWebRTCBinPadClass @@ -53,6 +56,14 @@ struct _GstWebRTCBinPadClass GstGhostPadClass parent_class; }; +G_DECLARE_FINAL_TYPE (GstWebRTCBinSinkPad, gst_webrtc_bin_sink_pad, GST, + WEBRTC_BIN_SINK_PAD, GstWebRTCBinPad); +#define GST_TYPE_WEBRTC_BIN_SINK_PAD (gst_webrtc_bin_sink_pad_get_type()) + +G_DECLARE_FINAL_TYPE (GstWebRTCBinSrcPad, gst_webrtc_bin_src_pad, GST, + WEBRTC_BIN_SRC_PAD, GstWebRTCBinPad); +#define GST_TYPE_WEBRTC_BIN_SRC_PAD (gst_webrtc_bin_src_pad_get_type()) + GType gst_webrtc_bin_get_type(void); #define GST_TYPE_WEBRTC_BIN (gst_webrtc_bin_get_type()) #define GST_WEBRTC_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_BIN,GstWebRTCBin)) diff --git a/subprojects/gst-plugins-bad/ext/webrtc/utils.c b/subprojects/gst-plugins-bad/ext/webrtc/utils.c index 5b6dd08fa4..f0741d1e55 100644 --- a/subprojects/gst-plugins-bad/ext/webrtc/utils.c +++ b/subprojects/gst-plugins-bad/ext/webrtc/utils.c @@ -223,3 +223,28 @@ webrtc_kind_from_caps (const GstCaps * caps) return GST_WEBRTC_KIND_UNKNOWN; } + +char * +_get_msid_from_media (const GstSDPMedia * media) +{ + int i; + + for (i = 0; i < gst_sdp_media_attributes_len (media); i++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i); + const char *start, *end; + + if (!attr->value) + continue; + + start = strstr (attr->value, "msid:"); + if (!start) + continue; + + start += strlen ("msid:"); + end = strstr (start, " "); + if (end) + return g_strndup (start, end - start); + } + + return NULL; +} diff --git a/subprojects/gst-plugins-bad/ext/webrtc/utils.h b/subprojects/gst-plugins-bad/ext/webrtc/utils.h index 9d4e1ee3dd..e5d3d124ab 100644 --- a/subprojects/gst-plugins-bad/ext/webrtc/utils.h +++ b/subprojects/gst-plugins-bad/ext/webrtc/utils.h @@ -66,6 +66,8 @@ G_GNUC_INTERNAL GstCaps * _rtp_caps_from_media (const GstSDPMedia * media); G_GNUC_INTERNAL GstWebRTCKind webrtc_kind_from_caps (const GstCaps * caps); +G_GNUC_INTERNAL +char * _get_msid_from_media (const GstSDPMedia * media); #define gst_webrtc_kind_to_string(kind) _enum_value_to_string(GST_TYPE_WEBRTC_KIND, kind) #define gst_webrtc_rtp_transceiver_direction_to_string(dir) _enum_value_to_string(GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, dir) diff --git a/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c b/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c index 42c759c958..b9d1b83d5a 100644 --- a/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c +++ b/subprojects/gst-plugins-bad/tests/check/elements/webrtcbin.c @@ -47,7 +47,7 @@ typedef enum { - STATE_NEW, + STATE_NEW = 1, STATE_NEGOTIATION_NEEDED, STATE_OFFER_CREATED, STATE_OFFER_SET, @@ -776,27 +776,56 @@ test_webrtc_create_offer (struct test_webrtc *t) } static TestState -test_webrtc_wait_for_state_mask (struct test_webrtc *t, TestState state) +test_webrtc_check_for_state_mask_unlocked (struct test_webrtc *t, + TestState state) { guint i; + GST_LOG ("attempting to check for state mask 0x%x", state); + for (i = 0; i < t->states->len; i++) { + TestState val = g_array_index (t->states, TestState, i); + + if (((1 << val) & state) != 0) { + GST_DEBUG ("found state 0x%x in wait mask 0x%x at idx %u", val, state, i); + g_array_remove_range (t->states, 0, i + 1); + return val; + } + } + + return 0; +} + +static TestState +test_webrtc_check_for_state_mask (struct test_webrtc *t, TestState state) +{ + TestState ret; + + g_mutex_lock (&t->lock); + ret = test_webrtc_check_for_state_mask_unlocked (t, state); + g_mutex_unlock (&t->lock); + + return ret; +} + +static TestState +test_webrtc_wait_for_state_mask (struct test_webrtc *t, TestState state) +{ + TestState ret = 0; + g_mutex_lock (&t->lock); GST_LOG ("attempting to wait for state mask 0x%x", state); while (TRUE) { - for (i = 0; i < t->states->len; i++) { - TestState val = g_array_index (t->states, TestState, i); + ret = test_webrtc_check_for_state_mask_unlocked (t, state); + + if (ret) + break; - if (((1 << val) & state) != 0) { - GST_DEBUG ("found state 0x%x in wait mask 0x%x at idx %u", val, state, - i); - g_array_remove_range (t->states, 0, i + 1); - g_mutex_unlock (&t->lock); - return val; - } - } g_cond_wait (&t->cond, &t->lock); } + g_mutex_unlock (&t->lock); + + return ret; } static TestState @@ -3862,7 +3891,7 @@ GST_START_TEST (test_codec_preferences_incompatible_extmaps) t->on_ice_candidate = NULL; t->on_offer_created = offer_created_produced_error; - test_validate_sdp_full (t, NULL, NULL, STATE_OFFER_CREATED, TRUE); + test_validate_sdp_full (t, NULL, NULL, 1 << STATE_ERROR, TRUE); test_webrtc_free (t); } @@ -3895,7 +3924,7 @@ GST_START_TEST (test_codec_preferences_invalid_extmap) t->on_ice_candidate = NULL; t->on_offer_created = offer_created_produced_error; - test_validate_sdp_full (t, NULL, NULL, STATE_OFFER_CREATED, TRUE); + test_validate_sdp_full (t, NULL, NULL, 1 << STATE_ERROR, TRUE); test_webrtc_free (t); } @@ -5540,6 +5569,141 @@ GST_START_TEST (test_data_channel_recreate_offer) GST_END_TEST; +static void +validate_msid (struct test_webrtc *t, GstElement * element, + GstWebRTCSessionDescription * desc, gpointer user_data) +{ + char **expected_msid = user_data; + int i; + + for (i = 0; i < gst_sdp_message_medias_len (desc->sdp); i++) { + const GstSDPMedia *media = gst_sdp_message_get_media (desc->sdp, i); + gboolean have_msid = FALSE; + char *prev_msid = NULL; + int j; + + for (j = 0; j < gst_sdp_media_attributes_len (media); j++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j); + const char *start; + + if (!attr->value) + continue; + + start = strstr (attr->value, "msid:"); + if (start) { + const char *end; + char *msid; + + start += strlen ("msid:"); + end = strstr (start, " "); + msid = g_strndup (start, end - start); + fail_unless (end, "Invalid msid attribute"); + fail_if (have_msid && g_strcmp0 (prev_msid, msid) != 0, + "different values for multiple msid values at mline %u, " + "prev msid %s, msid %s", i, prev_msid, msid); + have_msid = TRUE; + fail_unless_equals_string (msid, expected_msid[i]); + g_clear_pointer (&prev_msid, g_free); + prev_msid = msid; + } + } + g_clear_pointer (&prev_msid, g_free); + fail_unless (have_msid, "no msid attribute in media %u", i); + } +} + +static void +_pad_added_src_check_msid (struct test_webrtc *t, GstElement * element, + GstPad * pad, gpointer user_data) +{ + const char *expected_msid = user_data; + char *msid; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC) + return; + + g_object_get (pad, "msid", &msid, NULL); + fail_unless_equals_string (msid, expected_msid); + g_clear_pointer (&msid, g_free); + + test_webrtc_signal_state_unlocked (t, STATE_CUSTOM); +} + +GST_START_TEST (test_msid) +{ + struct test_webrtc *t = create_audio_test (); + VAL_SDP_INIT (no_duplicate_payloads, on_sdp_media_no_duplicate_payloads, + NULL, NULL); + guint media_format_count[] = { 1, 5 }; + VAL_SDP_INIT (media_formats, on_sdp_media_count_formats, + media_format_count, &no_duplicate_payloads); + VAL_SDP_INIT (count, _count_num_sdp_media, GUINT_TO_POINTER (2), + &media_formats); + const gchar *expected_offer_msid[] = { "a1", "a1", }; + VAL_SDP_INIT (offer_msid, validate_msid, expected_offer_msid, &count); + const gchar *expected_offer_setup[] = { "actpass", "actpass", }; + VAL_SDP_INIT (offer_setup, on_sdp_media_setup, expected_offer_setup, + &offer_msid); + const gchar *expected_offer_direction[] = { "sendrecv", "sendrecv", }; + VAL_SDP_INIT (offer, on_sdp_media_direction, expected_offer_direction, + &offer_setup); + const gchar *expected_answer_setup[] = { "active", "active", }; + VAL_SDP_INIT (answer_setup, on_sdp_media_setup, expected_answer_setup, + &count); + const gchar *expected_answer_direction[] = { "recvonly", "recvonly", }; + VAL_SDP_INIT (answer, on_sdp_media_direction, expected_answer_direction, + &answer_setup); + GstPad *pad; + GstHarness *src; + GstElement *rtpbin2; + + t->on_pad_added = _pad_added_src_check_msid; + t->pad_added_data = (gpointer) "a1"; + + rtpbin2 = gst_bin_get_by_name (GST_BIN (t->webrtc2), "rtpbin"); + fail_unless (rtpbin2 != NULL); + g_signal_connect (rtpbin2, "new-jitterbuffer", + G_CALLBACK (new_jitterbuffer_set_fast_start), NULL); + g_object_unref (rtpbin2); + + g_signal_connect (t->webrtc1, "on-new-transceiver", + G_CALLBACK (on_new_transceiver_set_rtx_fec), NULL); + g_signal_connect (t->webrtc2, "on-new-transceiver", + G_CALLBACK (on_new_transceiver_set_rtx_fec), NULL); + + src = gst_harness_new_with_element (t->webrtc1, "sink_1", NULL); + add_audio_test_src_harness (src, 0x12345678); + t->harnesses = g_list_prepend (t->harnesses, src); + + pad = gst_element_get_static_pad (t->webrtc1, "sink_0"); + g_object_set (pad, "msid", "a1", NULL); + gst_clear_object (&pad); + + pad = gst_element_get_static_pad (t->webrtc1, "sink_1"); + g_object_set (pad, "msid", "a1", NULL); + gst_clear_object (&pad); + + test_validate_sdp (t, &offer, &answer); + + fail_if (gst_element_set_state (t->webrtc1, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE); + fail_if (gst_element_set_state (t->webrtc2, + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE); + + while (TRUE) { + gst_harness_push_from_src (src); + + if (test_webrtc_check_for_state_mask (t, 1 << STATE_CUSTOM)) + break; + + g_usleep (10 * 1000); + } + + test_webrtc_free (t); +} + +GST_END_TEST; + static Suite * webrtcbin_suite (void) { @@ -5604,6 +5768,7 @@ webrtcbin_suite (void) tcase_add_test (tc, test_bundle_multiple_media_rtx_payload_mapping); tcase_add_test (tc, test_invalid_add_media_in_answer); tcase_add_test (tc, test_add_turn_server); + tcase_add_test (tc, test_msid); if (sctpenc && sctpdec) { tcase_add_test (tc, test_data_channel_create); tcase_add_test (tc, test_data_channel_remote_notify);