2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31 #include "webrtcdatachannel.h"
32 #include "webrtcsctptransport.h"
34 #include "gst/webrtc/webrtc-priv.h"
35 #include <gst/webrtc/nice/nice.h>
36 #include <gst/rtp/rtp.h>
42 #define RANDOM_SESSION_ID \
43 ((((((guint64) g_random_int()) << 32) | \
44 (guint64) g_random_int ())) & \
45 G_GUINT64_CONSTANT (0x7fffffffffffffff))
47 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
48 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
49 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
51 #define PC_GET_COND(w) (&w->priv->pc_cond)
52 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
53 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
54 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
56 #define ICE_GET_LOCK(w) (&w->priv->ice_lock)
57 #define ICE_LOCK(w) (g_mutex_lock (ICE_GET_LOCK(w)))
58 #define ICE_UNLOCK(w) (g_mutex_unlock (ICE_GET_LOCK(w)))
60 #define DC_GET_LOCK(w) (&w->priv->dc_lock)
61 #define DC_LOCK(w) (g_mutex_lock (DC_GET_LOCK(w)))
62 #define DC_UNLOCK(w) (g_mutex_unlock (DC_GET_LOCK(w)))
64 /* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */
65 #define RTPSTORAGE_EXTRA_TIME (50)
67 #define DEFAULT_JB_LATENCY 200
69 #define RTPHDREXT_MID GST_RTP_HDREXT_BASE "sdes:mid"
70 #define RTPHDREXT_STREAM_ID GST_RTP_HDREXT_BASE "sdes:rtp-stream-id"
71 #define RTPHDREXT_REPAIRED_STREAM_ID GST_RTP_HDREXT_BASE "sdes:repaired-rtp-stream-id"
74 * SECTION: element-webrtcbin
77 * This webrtcbin implements the majority of the W3's peerconnection API and
78 * implementation guide where possible. Generating offers, answers and setting
79 * local and remote SDP's are all supported. Both media descriptions and
80 * descriptions involving data channels are supported.
82 * Each input/output pad is equivalent to a Track in W3 parlance which are
83 * added/removed from the bin. The number of requested sink pads is the number
84 * of streams that will be sent to the receiver and will be associated with a
85 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
87 * On the receiving side, RTPTransceiver's are created in response to setting
88 * a remote description. Output pads for the receiving streams in the set
89 * description are also created when data is received.
91 * A TransportStream is created when needed in order to transport the data over
92 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
93 * on the negotiated SDP's between the peers based on the bundle and rtcp
94 * configuration. Some cases are outlined below for a simple single
95 * audio/video/data session:
97 * - max-bundle uses a single transport for all
98 * media/data transported. Renegotiation involves adding/removing the
99 * necessary streams to the existing transports.
100 * - max-compat involves two TransportStream per media stream
101 * to transport the rtp and the rtcp packets and a single TransportStream for
102 * all data channels. Each stream change involves modifying the associated
103 * TransportStream/s as necessary.
108 * assert sending payload type matches the stream
109 * reconfiguration (of anything)
111 * balanced bundle policy
112 * setting custom DTLS certificates
114 * separate session id's from mlineindex properly
115 * how to deal with replacing a input/output track/stream
118 static void _update_need_negotiation (GstWebRTCBin * webrtc);
119 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
120 GstWebRTCBinPad * pad);
123 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
124 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
126 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
129 GST_STATIC_CAPS ("application/x-rtp"));
131 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
134 GST_STATIC_CAPS ("application/x-rtp"));
138 PROP_PAD_TRANSCEIVER = 1,
142 _have_nice_elements (GstWebRTCBin * webrtc)
144 GstPluginFeature *feature;
146 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
148 gst_object_unref (feature);
150 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
151 ("%s", "libnice elements are not available"));
155 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
157 gst_object_unref (feature);
159 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
160 ("%s", "libnice elements are not available"));
168 _have_sctp_elements (GstWebRTCBin * webrtc)
170 GstPluginFeature *feature;
172 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
174 gst_object_unref (feature);
176 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
177 ("%s", "sctp elements are not available"));
181 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
183 gst_object_unref (feature);
185 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
186 ("%s", "sctp elements are not available"));
194 _have_dtls_elements (GstWebRTCBin * webrtc)
196 GstPluginFeature *feature;
198 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
200 gst_object_unref (feature);
202 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
203 ("%s", "dtls elements are not available"));
207 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
209 gst_object_unref (feature);
211 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
212 ("%s", "dtls elements are not available"));
220 _gst_element_accumulator (GSignalInvocationHint * ihint,
221 GValue * return_accu, const GValue * handler_return, gpointer dummy)
225 element = g_value_get_object (handler_return);
226 GST_DEBUG ("got element %" GST_PTR_FORMAT, element);
228 g_value_set_object (return_accu, element);
230 /* stop emission if we have an element */
231 return (element == NULL);
234 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
237 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
238 GValue * value, GParamSpec * pspec)
240 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
243 case PROP_PAD_TRANSCEIVER:
244 g_value_set_object (value, pad->trans);
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253 gst_webrtc_bin_pad_finalize (GObject * object)
255 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
257 gst_clear_object (&pad->trans);
258 gst_clear_caps (&pad->received_caps);
259 g_clear_pointer (&pad->msid, g_free);
261 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
265 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
267 GObjectClass *gobject_class = (GObjectClass *) klass;
269 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
270 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
272 g_object_class_install_property (gobject_class,
273 PROP_PAD_TRANSCEIVER,
274 g_param_spec_object ("transceiver", "Transceiver",
275 "Transceiver associated with this pad",
276 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
277 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
281 gst_webrtc_bin_pad_update_tos_event (GstWebRTCBinPad * wpad)
283 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
285 if (wpad->received_caps && trans->parent.mid) {
286 GstPad *pad = GST_PAD (wpad);
288 gst_event_take (&trans->tos_event,
289 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
290 gst_structure_new ("GstWebRtcBinUpdateTos", "mid", G_TYPE_STRING,
291 trans->parent.mid, NULL)));
293 GST_DEBUG_OBJECT (pad, "sending new tos event %" GST_PTR_FORMAT,
295 gst_pad_send_event (pad, gst_event_ref (trans->tos_event));
300 _get_pending_sink_transceiver (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
304 for (ret = webrtc->priv->pending_sink_transceivers; ret; ret = ret->next) {
305 if (ret->data == pad)
313 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
315 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
316 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
317 gboolean check_negotiation = FALSE;
319 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
322 gst_event_parse_caps (event, &caps);
323 check_negotiation = (!wpad->received_caps
324 || !gst_caps_is_equal (wpad->received_caps, caps));
325 gst_caps_replace (&wpad->received_caps, caps);
327 GST_DEBUG_OBJECT (parent,
328 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
329 GST_PTR_FORMAT, pad, check_negotiation, caps);
331 if (check_negotiation) {
332 gst_webrtc_bin_pad_update_tos_event (wpad);
335 /* A remote description might have been set while the pad hadn't
336 * yet received caps, delaying the connection of the input stream
340 GST_OBJECT_LOCK (wpad->trans);
341 if (wpad->trans->current_direction ==
342 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY
343 || wpad->trans->current_direction ==
344 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
345 GList *pending = _get_pending_sink_transceiver (webrtc, wpad);
348 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
349 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
350 wpad->trans, wpad->received_caps);
351 _connect_input_stream (webrtc, wpad);
352 gst_pad_remove_probe (GST_PAD (pad), wpad->block_id);
354 gst_object_unref (pending->data);
355 webrtc->priv->pending_sink_transceivers =
356 g_list_delete_link (webrtc->priv->pending_sink_transceivers,
360 GST_OBJECT_UNLOCK (wpad->trans);
363 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
364 check_negotiation = TRUE;
367 if (check_negotiation) {
369 _update_need_negotiation (webrtc);
373 return gst_pad_event_default (pad, parent, event);
377 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
379 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
380 gboolean ret = FALSE;
382 switch (GST_QUERY_TYPE (query)) {
383 case GST_QUERY_ACCEPT_CAPS:
384 GST_OBJECT_LOCK (wpad->trans);
385 if (wpad->trans->codec_preferences) {
388 gst_query_parse_accept_caps (query, &caps);
390 gst_query_set_accept_caps_result (query,
391 gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
394 GST_OBJECT_UNLOCK (wpad->trans);
399 GstCaps *codec_preferences = NULL;
401 GST_OBJECT_LOCK (wpad->trans);
402 if (wpad->trans->codec_preferences)
403 codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
404 GST_OBJECT_UNLOCK (wpad->trans);
406 if (codec_preferences) {
407 GstCaps *filter = NULL;
408 GstCaps *filter_prefs = NULL;
411 gst_query_parse_caps (query, &filter);
414 filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
415 GST_CAPS_INTERSECT_FIRST);
416 gst_caps_unref (codec_preferences);
418 filter_prefs = codec_preferences;
421 target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
425 result = gst_pad_query_caps (target, filter_prefs);
426 gst_query_set_caps_result (query, result);
427 gst_caps_unref (result);
429 gst_object_unref (target);
431 gst_query_set_caps_result (query, filter_prefs);
434 gst_caps_unref (filter_prefs);
446 return gst_pad_query_default (pad, parent, query);
451 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
455 static GstWebRTCBinPad *
456 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction,
459 GstWebRTCBinPad *pad;
460 GstPadTemplate *template;
463 if (direction == GST_PAD_SINK) {
464 template = gst_static_pad_template_get (&sink_template);
465 pad_type = GST_TYPE_WEBRTC_BIN_SINK_PAD;
466 } else if (direction == GST_PAD_SRC) {
467 template = gst_static_pad_template_get (&src_template);
468 pad_type = GST_TYPE_WEBRTC_BIN_SRC_PAD;
470 g_assert_not_reached ();
474 g_object_new (pad_type, "name", name, "direction",
475 direction, "template", template, NULL);
476 gst_object_unref (template);
480 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
481 direction == GST_PAD_SRC ? "src" : "sink");
487 PROP_SINK_PAD_MSID = 1,
491 * GstWebRTCBinSinkPad:
495 struct _GstWebRTCBinSinkPad
500 G_DEFINE_TYPE (GstWebRTCBinSinkPad, gst_webrtc_bin_sink_pad,
501 GST_TYPE_WEBRTC_BIN_PAD);
504 gst_webrtc_bin_sink_pad_get_property (GObject * object, guint prop_id,
505 GValue * value, GParamSpec * pspec)
507 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
510 case PROP_SINK_PAD_MSID:
511 g_value_set_string (value, pad->msid);
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520 gst_webrtc_bin_sink_pad_set_property (GObject * object, guint prop_id,
521 const GValue * value, GParamSpec * pspec)
523 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
526 case PROP_SINK_PAD_MSID:
528 pad->msid = g_value_dup_string (value);
531 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
537 gst_webrtc_bin_sink_pad_class_init (GstWebRTCBinSinkPadClass * klass)
539 GObjectClass *gobject_class = (GObjectClass *) klass;
541 gobject_class->get_property = gst_webrtc_bin_sink_pad_get_property;
542 gobject_class->set_property = gst_webrtc_bin_sink_pad_set_property;
545 * GstWebRTCBinSinkPad:msid:
547 * The MediaStream Identifier to use for this pad (MediaStreamTrack).
548 * Fallback is the RTP SDES cname value if not provided.
552 g_object_class_install_property (gobject_class,
554 g_param_spec_string ("msid", "MSID",
555 "Local MediaStream ID to use for this pad (NULL = unset)", NULL,
556 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
560 gst_webrtc_bin_sink_pad_init (GstWebRTCBinSinkPad * pad)
562 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
563 gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
568 PROP_SRC_PAD_MSID = 1,
572 * GstWebRTCBinSrcPad:
576 struct _GstWebRTCBinSrcPad
581 G_DEFINE_TYPE (GstWebRTCBinSrcPad, gst_webrtc_bin_src_pad,
582 GST_TYPE_WEBRTC_BIN_PAD);
585 gst_webrtc_bin_src_pad_get_property (GObject * object, guint prop_id,
586 GValue * value, GParamSpec * pspec)
588 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
591 case PROP_SRC_PAD_MSID:
592 g_value_set_string (value, pad->msid);
595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601 gst_webrtc_bin_src_pad_class_init (GstWebRTCBinSrcPadClass * klass)
603 GObjectClass *gobject_class = (GObjectClass *) klass;
605 gobject_class->get_property = gst_webrtc_bin_src_pad_get_property;
608 * GstWebRTCBinSrcPad:msid:
610 * The MediaStream Identifier the remote peer used for this pad (MediaStreamTrack).
611 * Will be NULL if not advertised in the remote SDP.
615 g_object_class_install_property (gobject_class,
617 g_param_spec_string ("msid", "MSID",
618 "Remote MediaStream ID in use for this pad (NULL = not advertised)",
619 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
623 gst_webrtc_bin_src_pad_init (GstWebRTCBinSrcPad * pad)
627 #define gst_webrtc_bin_parent_class parent_class
628 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
629 G_ADD_PRIVATE (GstWebRTCBin)
630 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
631 "webrtcbin element"););
637 CREATE_ANSWER_SIGNAL,
638 SET_LOCAL_DESCRIPTION_SIGNAL,
639 SET_REMOTE_DESCRIPTION_SIGNAL,
640 ADD_ICE_CANDIDATE_SIGNAL,
641 ON_NEGOTIATION_NEEDED_SIGNAL,
642 ON_ICE_CANDIDATE_SIGNAL,
643 ON_NEW_TRANSCEIVER_SIGNAL,
645 ADD_TRANSCEIVER_SIGNAL,
646 GET_TRANSCEIVER_SIGNAL,
647 GET_TRANSCEIVERS_SIGNAL,
648 ADD_TURN_SERVER_SIGNAL,
649 CREATE_DATA_CHANNEL_SIGNAL,
650 ON_DATA_CHANNEL_SIGNAL,
651 PREPARE_DATA_CHANNEL_SIGNAL,
659 PROP_CONNECTION_STATE,
660 PROP_SIGNALING_STATE,
661 PROP_ICE_GATHERING_STATE,
662 PROP_ICE_CONNECTION_STATE,
663 PROP_LOCAL_DESCRIPTION,
664 PROP_CURRENT_LOCAL_DESCRIPTION,
665 PROP_PENDING_LOCAL_DESCRIPTION,
666 PROP_REMOTE_DESCRIPTION,
667 PROP_CURRENT_REMOTE_DESCRIPTION,
668 PROP_PENDING_REMOTE_DESCRIPTION,
672 PROP_ICE_TRANSPORT_POLICY,
679 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
684 GstWebRTCICEStream *stream;
687 /* FIXME: locking? */
689 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
693 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
694 IceStreamItem *item =
695 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
697 if (item->session_id == session_id) {
698 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
699 "session %u", item->stream, session_id);
704 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
710 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
711 GstWebRTCICEStream * stream)
713 IceStreamItem item = { session_id, stream };
715 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
716 "session %u", stream, session_id);
717 g_array_append_val (webrtc->priv->ice_stream_map, item);
720 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
723 static GstWebRTCRTPTransceiver *
724 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
725 FindTransceiverFunc func)
729 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
730 GstWebRTCRTPTransceiver *transceiver =
731 g_ptr_array_index (webrtc->priv->transceivers, i);
733 if (func (transceiver, data))
741 transceiver_match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
743 return g_strcmp0 (trans->mid, mid) == 0;
747 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
752 return trans->mline == *mline;
755 static GstWebRTCRTPTransceiver *
756 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
758 GstWebRTCRTPTransceiver *trans;
760 trans = _find_transceiver (webrtc, &mlineindex,
761 (FindTransceiverFunc) transceiver_match_for_mline);
763 GST_TRACE_OBJECT (webrtc,
764 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
770 static GstWebRTCRTPTransceiver *
771 _find_transceiver_for_mid (GstWebRTCBin * webrtc, const char *mid)
773 GstWebRTCRTPTransceiver *trans;
775 trans = _find_transceiver (webrtc, mid,
776 (FindTransceiverFunc) transceiver_match_for_mid);
778 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT " for "
779 "mid %s", trans, mid);
784 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
787 static TransportStream *
788 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
789 FindTransportFunc func)
793 for (i = 0; i < webrtc->priv->transports->len; i++) {
794 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
796 if (func (stream, data))
804 match_stream_for_session (TransportStream * trans, guint * session)
806 return trans->session_id == *session;
809 static TransportStream *
810 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
812 TransportStream *stream;
814 stream = _find_transport (webrtc, &session_id,
815 (FindTransportFunc) match_stream_for_session);
817 GST_TRACE_OBJECT (webrtc,
818 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
823 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
825 static GstWebRTCBinPad *
826 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
828 GstElement *element = GST_ELEMENT (webrtc);
831 GST_OBJECT_LOCK (webrtc);
833 for (; l; l = g_list_next (l)) {
834 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
836 if (func (l->data, data)) {
837 gst_object_ref (l->data);
838 GST_OBJECT_UNLOCK (webrtc);
843 l = webrtc->priv->pending_pads;
844 for (; l; l = g_list_next (l)) {
845 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
847 if (func (l->data, data)) {
848 gst_object_ref (l->data);
849 GST_OBJECT_UNLOCK (webrtc);
853 GST_OBJECT_UNLOCK (webrtc);
858 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
861 static WebRTCDataChannel *
862 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
863 FindDataChannelFunc func)
867 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
868 WebRTCDataChannel *channel =
869 g_ptr_array_index (webrtc->priv->data_channels, i);
871 if (func (channel, data))
879 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
881 return channel->parent.id == *id;
884 /* always called with dc_lock held */
885 static WebRTCDataChannel *
886 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
888 WebRTCDataChannel *channel;
890 channel = _find_data_channel (webrtc, &id,
891 (FindDataChannelFunc) data_channel_match_for_id);
893 GST_TRACE_OBJECT (webrtc,
894 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
900 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
902 GST_OBJECT_LOCK (webrtc);
903 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
904 GST_OBJECT_UNLOCK (webrtc);
908 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
910 gboolean ret = FALSE;
913 GST_OBJECT_LOCK (webrtc);
914 l = g_list_find (webrtc->priv->pending_pads, pad);
916 webrtc->priv->pending_pads =
917 g_list_remove_link (webrtc->priv->pending_pads, l);
921 GST_OBJECT_UNLOCK (webrtc);
927 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
929 _remove_pending_pad (webrtc, pad);
931 if (webrtc->priv->running)
932 gst_pad_set_active (GST_PAD (pad), TRUE);
933 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
937 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
939 _remove_pending_pad (webrtc, pad);
941 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
946 GstPadDirection direction;
951 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
953 return GST_PAD_DIRECTION (pad) == match->direction
954 && pad->trans->mline == match->mline;
957 static GstWebRTCBinPad *
958 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
961 MLineMatch m = { direction, mline };
963 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
968 GstPadDirection direction;
969 GstWebRTCRTPTransceiver *trans;
973 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
975 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
978 static GstWebRTCBinPad *
979 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
980 GstWebRTCRTPTransceiver * trans)
982 TransMatch m = { direction, trans };
984 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
989 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
997 GstWebRTCRTPTransceiverDirection direction;
1002 mid_ssrc_match_for_ssrc (SsrcMapItem * entry, const struct SsrcMatch *match)
1004 return entry->direction == match->direction && entry->ssrc == match->ssrc;
1008 mid_ssrc_remove_ssrc (SsrcMapItem * item, const struct SsrcMatch *match)
1010 return !mid_ssrc_match_for_ssrc (item, match);
1013 static SsrcMapItem *
1014 find_mid_ssrc_for_ssrc (GstWebRTCBin * webrtc,
1015 GstWebRTCRTPTransceiverDirection direction, guint rtp_session, guint ssrc)
1017 TransportStream *stream = _find_transport_for_session (webrtc, rtp_session);
1018 struct SsrcMatch m = { direction, ssrc };
1023 return transport_stream_find_ssrc_map_item (stream, &m,
1024 (FindSsrcMapFunc) mid_ssrc_match_for_ssrc);
1027 static SsrcMapItem *
1028 find_or_add_ssrc_map_item (GstWebRTCBin * webrtc,
1029 GstWebRTCRTPTransceiverDirection direction, guint rtp_session, guint ssrc,
1032 TransportStream *stream = _find_transport_for_session (webrtc, rtp_session);
1033 struct SsrcMatch m = { direction, ssrc };
1039 if ((item = transport_stream_find_ssrc_map_item (stream, &m,
1040 (FindSsrcMapFunc) mid_ssrc_match_for_ssrc)))
1043 return transport_stream_add_ssrc_map_item (stream, direction, ssrc,
1048 remove_ssrc_entry_by_ssrc (GstWebRTCBin * webrtc, guint rtp_session, guint ssrc)
1050 TransportStream *stream;
1052 stream = _find_transport_for_session (webrtc, rtp_session);
1054 struct SsrcMatch m =
1055 { GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, ssrc };
1057 transport_stream_filter_ssrc_map_item (stream, &m,
1058 (FindSsrcMapFunc) mid_ssrc_remove_ssrc);
1060 m.direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
1061 transport_stream_filter_ssrc_map_item (stream, &m,
1062 (FindSsrcMapFunc) mid_ssrc_remove_ssrc);
1067 _unlock_pc_thread (GMutex * lock)
1069 g_mutex_unlock (lock);
1070 return G_SOURCE_REMOVE;
1074 _gst_pc_thread (GstWebRTCBin * webrtc)
1077 webrtc->priv->main_context = g_main_context_new ();
1078 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
1080 PC_COND_BROADCAST (webrtc);
1081 g_main_context_invoke (webrtc->priv->main_context,
1082 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
1084 /* Having the thread be the thread default GMainContext will break the
1085 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
1087 g_main_loop_run (webrtc->priv->loop);
1089 GST_OBJECT_LOCK (webrtc);
1090 g_main_context_unref (webrtc->priv->main_context);
1091 webrtc->priv->main_context = NULL;
1092 GST_OBJECT_UNLOCK (webrtc);
1095 g_main_loop_unref (webrtc->priv->loop);
1096 webrtc->priv->loop = NULL;
1097 PC_COND_BROADCAST (webrtc);
1104 _start_thread (GstWebRTCBin * webrtc)
1109 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
1110 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
1114 while (!webrtc->priv->loop)
1115 PC_COND_WAIT (webrtc);
1116 webrtc->priv->is_closed = FALSE;
1121 _stop_thread (GstWebRTCBin * webrtc)
1123 GST_OBJECT_LOCK (webrtc);
1124 webrtc->priv->is_closed = TRUE;
1125 GST_OBJECT_UNLOCK (webrtc);
1128 g_main_loop_quit (webrtc->priv->loop);
1129 while (webrtc->priv->loop)
1130 PC_COND_WAIT (webrtc);
1133 g_thread_unref (webrtc->priv->thread);
1137 _execute_op (GstWebRTCBinTask * op)
1141 PC_LOCK (op->webrtc);
1142 if (op->webrtc->priv->is_closed) {
1143 PC_UNLOCK (op->webrtc);
1147 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
1148 "webrtcbin is closed. aborting execution.");
1149 GstStructure *s = gst_structure_new ("application/x-gst-promise",
1150 "error", G_TYPE_ERROR, error, NULL);
1152 gst_promise_reply (op->promise, s);
1154 g_clear_error (&error);
1156 GST_DEBUG_OBJECT (op->webrtc,
1157 "Peerconnection is closed, aborting execution");
1161 s = op->op (op->webrtc, op->data);
1163 PC_UNLOCK (op->webrtc);
1166 gst_promise_reply (op->promise, s);
1168 gst_structure_free (s);
1171 return G_SOURCE_REMOVE;
1175 _free_op (GstWebRTCBinTask * op)
1178 op->notify (op->data);
1180 gst_promise_unref (op->promise);
1185 * @promise is for correctly signalling the failure case to the caller when
1186 * the user supplies it. Without passing it in, the promise would never
1187 * be replied to in the case that @webrtc becomes closed between the idle
1188 * source addition and the the execution of the idle source.
1191 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
1192 gpointer data, GDestroyNotify notify, GstPromise * promise)
1194 GstWebRTCBinTask *op;
1198 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
1200 GST_OBJECT_LOCK (webrtc);
1201 if (webrtc->priv->is_closed) {
1202 GST_OBJECT_UNLOCK (webrtc);
1203 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
1208 ctx = g_main_context_ref (webrtc->priv->main_context);
1209 GST_OBJECT_UNLOCK (webrtc);
1211 op = g_new0 (GstWebRTCBinTask, 1);
1212 op->webrtc = webrtc;
1215 op->notify = notify;
1217 op->promise = gst_promise_ref (promise);
1219 source = g_idle_source_new ();
1220 g_source_set_priority (source, G_PRIORITY_DEFAULT);
1221 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
1222 (GDestroyNotify) _free_op);
1223 g_source_attach (source, ctx);
1224 g_source_unref (source);
1225 g_main_context_unref (ctx);
1230 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
1231 static GstWebRTCICEConnectionState
1232 _collate_ice_connection_states (GstWebRTCBin * webrtc)
1234 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
1235 GstWebRTCICEConnectionState any_state = 0;
1236 gboolean all_new_or_closed = TRUE;
1237 gboolean all_completed_or_closed = TRUE;
1238 gboolean all_connected_completed_or_closed = TRUE;
1241 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1242 GstWebRTCRTPTransceiver *rtp_trans =
1243 g_ptr_array_index (webrtc->priv->transceivers, i);
1244 GstWebRTCICETransport *transport;
1245 GstWebRTCICEConnectionState ice_state;
1247 if (rtp_trans->stopped) {
1248 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1252 if (!rtp_trans->mid) {
1253 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1257 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
1259 /* get transport state */
1260 g_object_get (transport, "state", &ice_state, NULL);
1261 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
1263 any_state |= (1 << ice_state);
1265 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
1266 all_new_or_closed = FALSE;
1267 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
1268 all_completed_or_closed = FALSE;
1269 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
1270 && ice_state != STATE (CLOSED))
1271 all_connected_completed_or_closed = FALSE;
1274 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
1276 if (webrtc->priv->is_closed) {
1277 GST_TRACE_OBJECT (webrtc, "returning closed");
1278 return STATE (CLOSED);
1280 /* Any of the RTCIceTransports are in the failed state. */
1281 if (any_state & (1 << STATE (FAILED))) {
1282 GST_TRACE_OBJECT (webrtc, "returning failed");
1283 return STATE (FAILED);
1285 /* Any of the RTCIceTransports are in the disconnected state. */
1286 if (any_state & (1 << STATE (DISCONNECTED))) {
1287 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1288 return STATE (DISCONNECTED);
1290 /* All of the RTCIceTransports are in the new or closed state, or there are
1292 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
1293 GST_TRACE_OBJECT (webrtc, "returning new");
1296 /* Any of the RTCIceTransports are in the checking or new state. */
1297 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
1298 GST_TRACE_OBJECT (webrtc, "returning checking");
1299 return STATE (CHECKING);
1301 /* All RTCIceTransports are in the completed or closed state. */
1302 if (all_completed_or_closed) {
1303 GST_TRACE_OBJECT (webrtc, "returning completed");
1304 return STATE (COMPLETED);
1306 /* All RTCIceTransports are in the connected, completed or closed state. */
1307 if (all_connected_completed_or_closed) {
1308 GST_TRACE_OBJECT (webrtc, "returning connected");
1309 return STATE (CONNECTED);
1312 GST_FIXME ("unspecified situation, returning old state");
1313 return webrtc->ice_connection_state;
1317 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1318 static GstWebRTCICEGatheringState
1319 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1321 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1322 GstWebRTCICEGatheringState any_state = 0;
1323 GstWebRTCICEGatheringState ice_state;
1324 GstWebRTCDTLSTransport *dtls_transport;
1325 GstWebRTCICETransport *transport;
1326 gboolean all_completed = webrtc->priv->transceivers->len > 0 ||
1327 webrtc->priv->data_channel_transport;
1330 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1331 GstWebRTCRTPTransceiver *rtp_trans =
1332 g_ptr_array_index (webrtc->priv->transceivers, i);
1333 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1334 TransportStream *stream = trans->stream;
1336 if (rtp_trans->stopped || stream == NULL) {
1337 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1342 /* We only have a mid in the transceiver after we got the SDP answer,
1343 * which is usually long after gathering has finished */
1344 if (!rtp_trans->mid) {
1345 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1348 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1349 if (dtls_transport == NULL) {
1350 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1354 transport = dtls_transport->transport;
1356 /* get gathering state */
1357 g_object_get (transport, "gathering-state", &ice_state, NULL);
1358 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1360 any_state |= (1 << ice_state);
1361 if (ice_state != STATE (COMPLETE))
1362 all_completed = FALSE;
1365 /* check data channel transport gathering state */
1366 if (all_completed && webrtc->priv->data_channel_transport) {
1367 if ((dtls_transport = webrtc->priv->data_channel_transport->transport)) {
1368 transport = dtls_transport->transport;
1369 g_object_get (transport, "gathering-state", &ice_state, NULL);
1370 GST_TRACE_OBJECT (webrtc,
1371 "data channel transport %p gathering state: 0x%x", dtls_transport,
1373 any_state |= (1 << ice_state);
1374 if (ice_state != STATE (COMPLETE))
1375 all_completed = FALSE;
1379 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1381 /* Any of the RTCIceTransport s are in the gathering state. */
1382 if (any_state & (1 << STATE (GATHERING))) {
1383 GST_TRACE_OBJECT (webrtc, "returning gathering");
1384 return STATE (GATHERING);
1386 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1387 * the completed gathering state. */
1388 if (all_completed) {
1389 GST_TRACE_OBJECT (webrtc, "returning complete");
1390 return STATE (COMPLETE);
1393 /* Any of the RTCIceTransport s are in the new gathering state and none
1394 * of the transports are in the gathering state, or there are no transports. */
1395 GST_TRACE_OBJECT (webrtc, "returning new");
1400 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1401 static GstWebRTCPeerConnectionState
1402 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1404 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1405 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1406 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1407 GstWebRTCICEConnectionState any_ice_state = 0;
1408 GstWebRTCDTLSTransportState any_dtls_state = 0;
1409 gboolean ice_all_new_or_closed = TRUE;
1410 gboolean dtls_all_new_or_closed = TRUE;
1411 gboolean ice_all_new_connecting_or_checking = TRUE;
1412 gboolean dtls_all_new_connecting_or_checking = TRUE;
1413 gboolean ice_all_connected_completed_or_closed = TRUE;
1414 gboolean dtls_all_connected_completed_or_closed = TRUE;
1417 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1418 GstWebRTCRTPTransceiver *rtp_trans =
1419 g_ptr_array_index (webrtc->priv->transceivers, i);
1420 GstWebRTCDTLSTransport *transport;
1421 GstWebRTCICEConnectionState ice_state;
1422 GstWebRTCDTLSTransportState dtls_state;
1424 if (rtp_trans->stopped) {
1425 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1428 if (!rtp_trans->mid) {
1429 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1433 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1435 /* get transport state */
1436 g_object_get (transport, "state", &dtls_state, NULL);
1437 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1439 any_dtls_state |= (1 << dtls_state);
1441 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1442 dtls_all_new_or_closed = FALSE;
1443 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1444 dtls_all_new_connecting_or_checking = FALSE;
1445 if (dtls_state != DTLS_STATE (CONNECTED)
1446 && dtls_state != DTLS_STATE (CLOSED))
1447 dtls_all_connected_completed_or_closed = FALSE;
1449 g_object_get (transport->transport, "state", &ice_state, NULL);
1450 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1452 any_ice_state |= (1 << ice_state);
1454 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1455 ice_all_new_or_closed = FALSE;
1456 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1457 ice_all_new_connecting_or_checking = FALSE;
1458 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1459 && ice_state != ICE_STATE (CLOSED))
1460 ice_all_connected_completed_or_closed = FALSE;
1463 // also check data channel transport state
1464 if (webrtc->priv->data_channel_transport) {
1465 GstWebRTCDTLSTransport *transport =
1466 webrtc->priv->data_channel_transport->transport;
1467 GstWebRTCICEConnectionState ice_state;
1468 GstWebRTCDTLSTransportState dtls_state;
1470 g_object_get (transport, "state", &dtls_state, NULL);
1471 GST_TRACE_OBJECT (webrtc, "data channel transport DTLS state: 0x%x",
1473 any_dtls_state |= (1 << dtls_state);
1475 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1476 dtls_all_new_or_closed = FALSE;
1477 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1478 dtls_all_new_connecting_or_checking = FALSE;
1479 if (dtls_state != DTLS_STATE (CONNECTED)
1480 && dtls_state != DTLS_STATE (CLOSED))
1481 dtls_all_connected_completed_or_closed = FALSE;
1483 g_object_get (transport->transport, "state", &ice_state, NULL);
1484 GST_TRACE_OBJECT (webrtc, "data channel transport ICE state: 0x%x",
1486 any_ice_state |= (1 << ice_state);
1488 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1489 ice_all_new_or_closed = FALSE;
1490 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1491 ice_all_new_connecting_or_checking = FALSE;
1492 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1493 && ice_state != ICE_STATE (CLOSED))
1494 ice_all_connected_completed_or_closed = FALSE;
1497 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1498 "state: 0x%x", any_ice_state, any_dtls_state);
1500 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1501 if (webrtc->priv->is_closed) {
1502 GST_TRACE_OBJECT (webrtc, "returning closed");
1503 return STATE (CLOSED);
1506 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1507 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1508 GST_TRACE_OBJECT (webrtc, "returning failed");
1509 return STATE (FAILED);
1511 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1512 GST_TRACE_OBJECT (webrtc, "returning failed");
1513 return STATE (FAILED);
1516 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1518 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1519 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1520 return STATE (DISCONNECTED);
1523 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1524 * state, or there are no transports. */
1525 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1526 || webrtc->priv->transports->len == 0) {
1527 GST_TRACE_OBJECT (webrtc, "returning new");
1531 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1532 * or checking state. */
1533 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1534 GST_TRACE_OBJECT (webrtc, "returning connecting");
1535 return STATE (CONNECTING);
1538 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1539 * completed or closed state. */
1540 if (dtls_all_connected_completed_or_closed
1541 && ice_all_connected_completed_or_closed) {
1542 GST_TRACE_OBJECT (webrtc, "returning connected");
1543 return STATE (CONNECTED);
1546 /* FIXME: Unspecified state that happens for us */
1547 if ((dtls_all_new_connecting_or_checking
1548 || dtls_all_connected_completed_or_closed)
1549 && (ice_all_new_connecting_or_checking
1550 || ice_all_connected_completed_or_closed)) {
1551 GST_TRACE_OBJECT (webrtc, "returning connecting");
1552 return STATE (CONNECTING);
1555 GST_FIXME_OBJECT (webrtc,
1556 "Undefined situation detected, returning old state");
1557 return webrtc->peer_connection_state;
1563 static GstStructure *
1564 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1566 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1567 GstWebRTCICEGatheringState new_state;
1569 new_state = _collate_ice_gathering_states (webrtc);
1571 /* If the new state is complete, before we update the public state,
1572 * check if anyone published more ICE candidates while we were collating
1573 * and stop if so, because it means there's a new later
1574 * ice_gathering_state_task queued */
1575 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1577 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1578 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1579 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1581 ICE_UNLOCK (webrtc);
1584 if (new_state != webrtc->ice_gathering_state) {
1585 const gchar *old_s, *new_s;
1587 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1589 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1591 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1592 old_s, old_state, new_s, new_state);
1594 webrtc->ice_gathering_state = new_state;
1596 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1604 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1606 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1610 static GstStructure *
1611 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1613 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1614 GstWebRTCICEConnectionState new_state;
1616 new_state = _collate_ice_connection_states (webrtc);
1618 if (new_state != old_state) {
1619 const gchar *old_s, *new_s;
1621 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1623 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1625 GST_INFO_OBJECT (webrtc,
1626 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1629 webrtc->ice_connection_state = new_state;
1631 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1639 _update_ice_connection_state (GstWebRTCBin * webrtc)
1641 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1645 static GstStructure *
1646 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1648 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1649 GstWebRTCPeerConnectionState new_state;
1651 new_state = _collate_peer_connection_states (webrtc);
1653 if (new_state != old_state) {
1654 const gchar *old_s, *new_s;
1656 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1658 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1660 GST_INFO_OBJECT (webrtc,
1661 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1664 webrtc->peer_connection_state = new_state;
1666 g_object_notify (G_OBJECT (webrtc), "connection-state");
1674 _update_peer_connection_state (GstWebRTCBin * webrtc)
1676 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1681 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1684 gboolean res = FALSE;
1686 GST_OBJECT_LOCK (webrtc);
1687 l = GST_ELEMENT (webrtc)->pads;
1688 for (; l; l = g_list_next (l)) {
1689 GstWebRTCBinPad *wpad;
1691 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1694 wpad = GST_WEBRTC_BIN_PAD (l->data);
1695 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1696 && (!wpad->trans || !wpad->trans->stopped)) {
1697 if (wpad->trans && wpad->trans->codec_preferences) {
1705 l = webrtc->priv->pending_pads;
1706 for (; l; l = g_list_next (l)) {
1707 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1715 GST_OBJECT_UNLOCK (webrtc);
1719 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1721 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1725 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1727 /* We can't negotiate until we have received caps on all our sink pads,
1728 * as we will need the formats in our offer / answer */
1729 if (!_all_sinks_have_caps (webrtc)) {
1730 GST_LOG_OBJECT (webrtc,
1731 "no negotiation possible until caps have been received on all sink pads");
1735 /* If any implementation-specific negotiation is required, as described at
1736 * the start of this section, return "true".
1738 /* FIXME: emit when input caps/format changes? */
1740 if (!webrtc->current_local_description) {
1741 GST_LOG_OBJECT (webrtc, "no local description set");
1745 if (!webrtc->current_remote_description) {
1746 GST_LOG_OBJECT (webrtc, "no remote description set");
1750 /* If connection has created any RTCDataChannel's, and no m= section has
1751 * been negotiated yet for data, return "true". */
1752 if (webrtc->priv->data_channels->len > 0) {
1753 if (_message_get_datachannel_index (webrtc->current_local_description->
1754 sdp) >= G_MAXUINT) {
1755 GST_LOG_OBJECT (webrtc,
1756 "no data channel media section and have %u " "transports",
1757 webrtc->priv->data_channels->len);
1762 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1763 GstWebRTCRTPTransceiver *trans;
1765 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1767 if (trans->stopped) {
1768 /* FIXME: If t is stopped and is associated with an m= section according to
1769 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1770 * rejected in connection's currentLocalDescription or
1771 * currentRemoteDescription , return "true". */
1772 GST_FIXME_OBJECT (webrtc,
1773 "check if the transceiver is rejected in descriptions");
1775 const GstSDPMedia *media;
1776 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1778 if (trans->mline == -1 || trans->mid == NULL) {
1779 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1780 " mid %s", i, trans, trans->mid);
1783 /* internal inconsistency */
1784 g_assert (trans->mline <
1785 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1786 g_assert (trans->mline <
1787 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1789 /* FIXME: msid handling
1790 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1791 * section in connection's currentLocalDescription doesn't contain an
1792 * "a=msid" line, return "true". */
1795 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1797 local_dir = _get_direction_from_media (media);
1800 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1802 remote_dir = _get_direction_from_media (media);
1804 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1805 /* If connection's currentLocalDescription if of type "offer", and
1806 * the direction of the associated m= section in neither the offer
1807 * nor answer matches t's direction, return "true". */
1809 if (local_dir != trans->direction && remote_dir != trans->direction) {
1810 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1811 "description (local %s remote %s)",
1812 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction),
1813 gst_webrtc_rtp_transceiver_direction_to_string (local_dir),
1814 gst_webrtc_rtp_transceiver_direction_to_string (remote_dir));
1817 } else if (webrtc->current_local_description->type ==
1818 GST_WEBRTC_SDP_TYPE_ANSWER) {
1819 GstWebRTCRTPTransceiverDirection intersect_dir;
1821 /* If connection's currentLocalDescription if of type "answer", and
1822 * the direction of the associated m= section in the answer does not
1823 * match t's direction intersected with the offered direction (as
1824 * described in [JSEP] (section 5.3.1.)), return "true". */
1826 /* remote is the offer, local is the answer */
1827 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1829 if (intersect_dir != trans->direction) {
1830 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1831 "description intersected direction %s (local %s remote %s)",
1832 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction),
1833 gst_webrtc_rtp_transceiver_direction_to_string (local_dir),
1834 gst_webrtc_rtp_transceiver_direction_to_string (intersect_dir),
1835 gst_webrtc_rtp_transceiver_direction_to_string (remote_dir));
1842 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1846 static GstStructure *
1847 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1849 if (webrtc->priv->need_negotiation) {
1850 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1852 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1860 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1862 _update_need_negotiation (GstWebRTCBin * webrtc)
1864 /* If connection's [[isClosed]] slot is true, abort these steps. */
1865 if (webrtc->priv->is_closed)
1867 /* If connection's signaling state is not "stable", abort these steps. */
1868 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1871 /* If the result of checking if negotiation is needed is "false", clear the
1872 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1873 * to false, and abort these steps. */
1874 if (!_check_if_negotiation_is_needed (webrtc)) {
1875 webrtc->priv->need_negotiation = FALSE;
1878 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1879 if (webrtc->priv->need_negotiation)
1881 /* Set connection's [[needNegotiation]] slot to true. */
1882 webrtc->priv->need_negotiation = TRUE;
1883 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1884 * true, fire a simple event named negotiationneeded at connection. */
1885 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1890 _query_pad_caps (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * rtp_trans,
1891 GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
1896 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1897 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1899 /* Only return an error if actual empty caps were returned from the query. */
1900 if (gst_caps_is_empty (caps)) {
1901 g_set_error (error, GST_WEBRTC_ERROR,
1902 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
1903 "Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
1904 gst_clear_caps (&caps);
1905 gst_caps_unref (filter);
1909 n = gst_caps_get_size (caps);
1911 /* Make sure the caps are complete enough to figure out the media type and
1912 * encoding-name, otherwise they would match with basically any media. */
1913 caps = gst_caps_make_writable (caps);
1914 for (i = n; i > 0; i--) {
1915 const GstStructure *s = gst_caps_get_structure (caps, i - 1);
1917 if (!gst_structure_has_name (s, "application/x-rtp") ||
1918 !gst_structure_has_field (s, "media") ||
1919 !gst_structure_has_field (s, "encoding-name")) {
1920 gst_caps_remove_structure (caps, i - 1);
1925 /* If the filtering above resulted in empty caps, or the caps were ANY to
1926 * begin with, then don't report and error but just NULL.
1928 * This would be the case if negotiation would not fail but the peer does
1929 * not have any specific enough preferred caps that would allow us to
1932 if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
1933 GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
1934 gst_clear_caps (&caps);
1937 gst_caps_unref (filter);
1943 _find_codec_preferences (GstWebRTCBin * webrtc,
1944 GstWebRTCRTPTransceiver * rtp_trans, guint media_idx, GError ** error)
1946 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1947 GstCaps *ret = NULL;
1948 GstCaps *codec_preferences = NULL;
1949 GstWebRTCBinPad *pad = NULL;
1950 GstPadDirection direction;
1952 g_assert (rtp_trans);
1953 g_assert (error && *error == NULL);
1955 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1958 GST_OBJECT_LOCK (rtp_trans);
1959 if (rtp_trans->codec_preferences) {
1960 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1961 rtp_trans->codec_preferences);
1962 codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
1964 GST_OBJECT_UNLOCK (rtp_trans);
1966 if (rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
1967 direction = GST_PAD_SRC;
1969 direction = GST_PAD_SINK;
1971 pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
1973 /* try to find a pad */
1975 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1977 /* For the case where we have set our transceiver to sendrecv, but the
1978 * sink pad has not been requested yet.
1981 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1983 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1985 /* try to find a pad */
1987 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
1991 GstCaps *caps = NULL;
1993 if (pad->received_caps) {
1994 caps = gst_caps_ref (pad->received_caps);
1996 static GstStaticCaps static_filter =
1997 GST_STATIC_CAPS ("application/x-rtp, "
1998 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1999 GstCaps *filter = gst_static_caps_get (&static_filter);
2001 filter = gst_caps_make_writable (filter);
2003 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
2004 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
2005 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
2006 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
2008 caps = _query_pad_caps (webrtc, rtp_trans, pad, filter, error);
2015 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2016 GstWebRTCBinPad *srcpad =
2017 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
2020 caps = _query_pad_caps (webrtc, rtp_trans, srcpad, caps, error);
2021 gst_object_unref (srcpad);
2028 if (caps && codec_preferences) {
2029 GstCaps *intersection;
2031 intersection = gst_caps_intersect_full (codec_preferences, caps,
2032 GST_CAPS_INTERSECT_FIRST);
2033 gst_clear_caps (&caps);
2035 if (gst_caps_is_empty (intersection)) {
2036 g_set_error (error, GST_WEBRTC_ERROR,
2037 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
2038 "Caps negotiation on pad %s failed against codec preferences",
2039 GST_PAD_NAME (pad));
2040 gst_clear_caps (&intersection);
2042 caps = intersection;
2048 gst_caps_replace (&trans->last_retrieved_caps, caps);
2055 if (codec_preferences)
2056 ret = gst_caps_ref (codec_preferences);
2057 else if (trans->last_retrieved_caps)
2058 ret = gst_caps_ref (trans->last_retrieved_caps);
2064 gst_object_unref (pad);
2065 if (codec_preferences)
2066 gst_caps_unref (codec_preferences);
2069 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
2075 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
2076 WebRTCTransceiver * trans, const GstCaps * caps)
2085 ret = gst_caps_make_writable (caps);
2087 kind = webrtc_kind_from_caps (ret);
2088 for (i = 0; i < gst_caps_get_size (ret); i++) {
2089 GstStructure *s = gst_caps_get_structure (ret, i);
2092 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
2093 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
2095 if (kind == GST_WEBRTC_KIND_VIDEO) {
2096 if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
2097 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
2098 if (!gst_structure_has_field (s, "rtcp-fb-ccm-fir"))
2099 gst_structure_set (s, "rtcp-fb-ccm-fir", G_TYPE_BOOLEAN, TRUE, NULL);
2101 if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
2102 gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
2104 /* FIXME: codec-specific parameters? */
2111 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
2112 GParamSpec * pspec, GstWebRTCBin * webrtc)
2114 _update_ice_connection_state (webrtc);
2115 _update_peer_connection_state (webrtc);
2119 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
2120 GParamSpec * pspec, GstWebRTCBin * webrtc)
2122 _update_ice_gathering_state (webrtc);
2126 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
2127 GParamSpec * pspec, GstWebRTCBin * webrtc)
2129 _update_peer_connection_state (webrtc);
2133 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
2134 gboolean early, gpointer user_data)
2136 GstWebRTCBin *webrtc = user_data;
2137 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
2138 GstRTCPPacket packet;
2140 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
2143 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
2144 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
2146 GstWebRTCRTPTransceiver *rtp_trans = NULL;
2147 WebRTCTransceiver *trans;
2151 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
2154 GPOINTER_TO_UINT (g_object_get_data (internal_session,
2155 "GstWebRTCBinRTPSessionID"));
2157 mid = find_mid_ssrc_for_ssrc (webrtc,
2158 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session, ssrc);
2159 if (mid && mid->mid) {
2160 rtp_trans = _find_transceiver_for_mid (webrtc, mid->mid);
2161 GST_LOG_OBJECT (webrtc, "found %" GST_PTR_FORMAT " from mid entry "
2162 "using rtp session %u ssrc %u -> mid \'%s\'", rtp_trans,
2163 rtp_session, ssrc, mid->mid);
2165 trans = (WebRTCTransceiver *) rtp_trans;
2167 if (rtp_trans && rtp_trans->sender && trans->tos_event) {
2169 gchar *pad_name = NULL;
2172 g_strdup_printf ("send_rtcp_src_%u",
2173 rtp_trans->sender->transport->session_id);
2174 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
2177 gst_pad_push_event (pad, gst_event_ref (trans->tos_event));
2178 gst_object_unref (pad);
2184 gst_rtcp_buffer_unmap (&rtcp);
2187 /* False means we don't care about suppression */
2192 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
2194 GObject *internal_session = NULL;
2196 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
2197 session_id, &internal_session);
2199 if (internal_session) {
2200 g_object_set_data (internal_session, "GstWebRTCBinRTPSessionID",
2201 GUINT_TO_POINTER (session_id));
2202 g_signal_connect (internal_session, "on-sending-rtcp",
2203 G_CALLBACK (_on_sending_rtcp), webrtc);
2204 g_object_unref (internal_session);
2209 weak_free (GWeakRef * weak)
2211 g_weak_ref_clear (weak);
2215 static GstPadProbeReturn
2216 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
2218 GstWebRTCBin *webrtc = g_weak_ref_get ((GWeakRef *) user_data);
2221 return GST_PAD_PROBE_REMOVE;
2223 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
2224 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
2225 const GstStructure *s =
2226 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
2228 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
2232 if ((mid = gst_structure_get_string (s, "mid"))) {
2233 GstWebRTCRTPTransceiver *rtp_trans;
2235 rtp_trans = _find_transceiver_for_mid (webrtc, mid);
2237 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
2238 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
2239 trans->stream->session_id);
2242 /* Set DSCP field based on
2243 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2245 switch (rtp_trans->sender->priority) {
2246 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2249 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2252 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2253 switch (rtp_trans->kind) {
2254 case GST_WEBRTC_KIND_AUDIO:
2257 case GST_WEBRTC_KIND_VIDEO:
2258 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
2260 case GST_WEBRTC_KIND_UNKNOWN:
2265 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2266 switch (rtp_trans->kind) {
2267 case GST_WEBRTC_KIND_AUDIO:
2270 case GST_WEBRTC_KIND_VIDEO:
2271 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
2273 case GST_WEBRTC_KIND_UNKNOWN:
2280 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
2282 } else if (gst_structure_get_enum (s, "sctp-priority",
2283 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
2286 /* Set DSCP field based on
2287 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2290 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2293 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2296 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2297 dscp = 10; /* AF11 */
2299 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2300 dscp = 18; /* AF21 */
2303 if (webrtc->priv->data_channel_transport)
2304 gst_webrtc_ice_set_tos (webrtc->priv->ice,
2305 webrtc->priv->data_channel_transport->stream, dscp << 2);
2310 gst_object_unref (webrtc);
2312 return GST_PAD_PROBE_OK;
2315 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
2318 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
2320 GstWebRTCPriorityType sctp_priority = 0;
2323 if (!webrtc->priv->sctp_transport)
2327 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2328 GstWebRTCDataChannel *channel
2329 = g_ptr_array_index (webrtc->priv->data_channels, i);
2331 sctp_priority = MAX (sctp_priority, channel->priority);
2335 /* Default priority is low means DSCP field is left as 0 */
2336 if (sctp_priority == 0)
2337 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
2339 /* Nobody asks for DSCP, leave it as-is */
2340 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
2341 !webrtc->priv->tos_attached)
2344 /* If one stream has a non-default priority, then everyone else does too */
2345 gst_webrtc_bin_attach_tos (webrtc);
2347 webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
2352 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
2353 GstWebRTCICETransport * transport)
2358 pad = gst_element_get_static_pad (transport->sink, "sink");
2360 weak = g_new0 (GWeakRef, 1);
2361 g_weak_ref_init (weak, webrtc);
2363 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2364 _nicesink_pad_probe, weak, (GDestroyNotify) weak_free);
2365 gst_object_unref (pad);
2369 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
2373 if (webrtc->priv->tos_attached)
2375 webrtc->priv->tos_attached = TRUE;
2377 for (i = 0; i < webrtc->priv->transports->len; i++) {
2378 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
2380 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
2382 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
2383 stream->transport->transport);
2386 gst_webrtc_bin_update_sctp_priority (webrtc);
2389 static WebRTCTransceiver *
2390 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
2391 GstWebRTCRTPTransceiverDirection direction, guint mline, GstWebRTCKind kind,
2392 GstCaps * codec_preferences)
2394 WebRTCTransceiver *trans;
2395 GstWebRTCRTPTransceiver *rtp_trans;
2396 GstWebRTCRTPSender *sender;
2397 GstWebRTCRTPReceiver *receiver;
2399 sender = gst_webrtc_rtp_sender_new ();
2400 receiver = gst_webrtc_rtp_receiver_new ();
2401 trans = webrtc_transceiver_new (webrtc, sender, receiver);
2402 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2403 rtp_trans->direction = direction;
2404 rtp_trans->mline = mline;
2405 rtp_trans->kind = kind;
2406 rtp_trans->codec_preferences =
2407 codec_preferences ? gst_caps_ref (codec_preferences) : NULL;
2408 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
2409 rtp_trans->stopped = FALSE;
2411 GST_LOG_OBJECT (webrtc, "created new transceiver %" GST_PTR_FORMAT " with "
2412 "direction %s (%d), mline %u, kind %s (%d)", rtp_trans,
2413 gst_webrtc_rtp_transceiver_direction_to_string (direction), direction,
2414 mline, gst_webrtc_kind_to_string (kind), kind);
2416 g_signal_connect_object (sender, "notify::priority",
2417 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
2419 g_ptr_array_add (webrtc->priv->transceivers, trans);
2421 gst_object_unref (sender);
2422 gst_object_unref (receiver);
2424 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
2430 static TransportStream *
2431 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2433 GstWebRTCDTLSTransport *transport;
2434 TransportStream *ret;
2437 /* FIXME: how to parametrize the sender and the receiver */
2438 ret = transport_stream_new (webrtc, session_id);
2439 transport = ret->transport;
2441 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2442 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2443 g_signal_connect (G_OBJECT (transport->transport),
2444 "notify::gathering-state",
2445 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2446 g_signal_connect (G_OBJECT (transport), "notify::state",
2447 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2448 if (webrtc->priv->tos_attached)
2449 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2451 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2452 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2453 g_ptr_array_add (webrtc->priv->transports, ret);
2455 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2456 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2457 GST_ELEMENT (webrtc->rtpbin), pad_name))
2458 g_warn_if_reached ();
2461 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2462 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2463 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2464 g_warn_if_reached ();
2467 GST_TRACE_OBJECT (webrtc,
2468 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2473 static TransportStream *
2474 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2476 TransportStream *ret;
2478 ret = _find_transport_for_session (webrtc, session_id);
2481 ret = _create_transport_channel (webrtc, session_id);
2483 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2484 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2489 /* this is called from the webrtc thread with the pc lock held */
2491 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2492 GParamSpec * pspec, GstWebRTCBin * webrtc)
2494 GstWebRTCDataChannelState ready_state;
2496 g_object_get (channel, "ready-state", &ready_state, NULL);
2498 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2502 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2503 if (found == FALSE) {
2504 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2509 g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2512 gst_webrtc_bin_update_sctp_priority (webrtc);
2514 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2516 } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2520 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2521 || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2523 if (found == FALSE) {
2524 GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2531 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2532 GstWebRTCBin * webrtc)
2534 WebRTCDataChannel *channel;
2538 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2542 channel = _find_data_channel_for_id (webrtc, stream_id);
2544 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2545 channel->parent.id = stream_id;
2546 webrtc_data_channel_set_webrtcbin (channel, webrtc);
2548 g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL],
2551 gst_bin_add (GST_BIN (webrtc), channel->src_bin);
2552 gst_bin_add (GST_BIN (webrtc), channel->sink_bin);
2554 gst_element_sync_state_with_parent (channel->src_bin);
2555 gst_element_sync_state_with_parent (channel->sink_bin);
2557 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2559 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2563 g_signal_connect (channel, "notify::ready-state",
2564 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2566 sink_pad = gst_element_get_static_pad (channel->sink_bin, "sink");
2567 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2568 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2569 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2570 gst_object_unref (sink_pad);
2574 _on_sctp_state_notify (WebRTCSCTPTransport * sctp, GParamSpec * pspec,
2575 GstWebRTCBin * webrtc)
2577 GstWebRTCSCTPTransportState state;
2579 g_object_get (sctp, "state", &state, NULL);
2581 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2584 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2587 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2588 WebRTCDataChannel *channel;
2590 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2592 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2594 if (!channel->parent.negotiated && !channel->opened)
2595 webrtc_data_channel_start_negotiation (channel);
2601 /* Forward declaration so we can easily disconnect the signal handler */
2602 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2603 GParamSpec * pspec, GstWebRTCBin * webrtc);
2605 static GstStructure *
2606 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2608 TransportStream *stream;
2609 GstWebRTCDTLSTransport *transport;
2610 GstWebRTCDTLSTransportState dtls_state;
2611 WebRTCSCTPTransport *sctp_transport;
2613 stream = webrtc->priv->data_channel_transport;
2614 transport = stream->transport;
2616 g_object_get (transport, "state", &dtls_state, NULL);
2617 /* Not connected yet so just return */
2618 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2619 GST_DEBUG_OBJECT (webrtc,
2620 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2624 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2625 sctp_transport = webrtc->priv->sctp_transport;
2627 /* Not locked state anymore so this was already taken care of before */
2628 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2631 /* Start up the SCTP elements now that the DTLS connection is established */
2632 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2633 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2635 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2636 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2638 if (sctp_transport->sctpdec_block_id) {
2639 GstPad *receive_srcpad;
2642 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2644 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2646 sctp_transport->sctpdec_block_id = 0;
2647 gst_object_unref (receive_srcpad);
2650 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2657 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2658 GParamSpec * pspec, GstWebRTCBin * webrtc)
2660 GstWebRTCDTLSTransportState dtls_state;
2662 g_object_get (transport, "state", &dtls_state, NULL);
2664 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2667 /* Connected now, so schedule a task to update the state of the SCTP
2669 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2670 gst_webrtc_bin_enqueue_task (webrtc,
2671 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2675 static GstPadProbeReturn
2676 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2678 /* Drop all events: we don't care about them and don't want to block on
2679 * them. Sticky events would be forwarded again later once we unblock
2680 * and we don't want to forward them here already because that might
2681 * cause a spurious GST_FLOW_FLUSHING */
2682 if (GST_IS_EVENT (info->data))
2683 return GST_PAD_PROBE_DROP;
2685 /* But block on any actual data-flow so we don't accidentally send that
2686 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2689 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2691 return GST_PAD_PROBE_OK;
2694 static TransportStream *
2695 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2697 if (!webrtc->priv->data_channel_transport) {
2698 TransportStream *stream;
2699 WebRTCSCTPTransport *sctp_transport;
2701 stream = _find_transport_for_session (webrtc, session_id);
2704 stream = _create_transport_channel (webrtc, session_id);
2706 webrtc->priv->data_channel_transport = stream;
2708 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2709 sctp_transport = webrtc_sctp_transport_new ();
2710 sctp_transport->transport =
2711 g_object_ref (webrtc->priv->data_channel_transport->transport);
2712 sctp_transport->webrtcbin = webrtc;
2714 /* Don't automatically start SCTP elements as part of webrtcbin. We
2715 * need to delay this until the DTLS transport is fully connected! */
2716 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2717 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2719 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2720 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2723 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2724 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2725 g_signal_connect (sctp_transport, "notify::state",
2726 G_CALLBACK (_on_sctp_state_notify), webrtc);
2728 if (sctp_transport->sctpdec_block_id == 0) {
2729 GstPad *receive_srcpad;
2731 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2733 sctp_transport->sctpdec_block_id =
2734 gst_pad_add_probe (receive_srcpad,
2735 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2736 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2737 gst_object_unref (receive_srcpad);
2740 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2741 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2742 g_warn_if_reached ();
2744 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2745 GST_ELEMENT (stream->send_bin), "data_sink"))
2746 g_warn_if_reached ();
2748 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2749 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2751 if (!webrtc->priv->sctp_transport) {
2752 /* Connect to the notify::state signal to get notified when the DTLS
2753 * connection is established. Only then can we start the SCTP elements */
2754 g_signal_connect (stream->transport, "notify::state",
2755 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2757 /* As this would be racy otherwise, also schedule a task that checks the
2758 * current state of the connection already without getting the signal
2760 gst_webrtc_bin_enqueue_task (webrtc,
2761 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2764 webrtc->priv->sctp_transport = sctp_transport;
2766 gst_webrtc_bin_update_sctp_priority (webrtc);
2769 return webrtc->priv->data_channel_transport;
2772 static TransportStream *
2773 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2774 gboolean is_datachannel)
2777 return _get_or_create_data_channel_transports (webrtc, session_id);
2779 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2782 struct media_payload_map_item
2792 media_payload_map_item_init (struct media_payload_map_item *item,
2795 item->media_pt = media_pt;
2796 item->red_pt = G_MAXUINT;
2797 item->rtx_pt = G_MAXUINT;
2798 item->ulpfec_pt = G_MAXUINT;
2799 item->red_rtx_pt = G_MAXUINT;
2802 static struct media_payload_map_item *
2803 find_payload_map_for_media_pt (GArray * media_mapping, guint media_pt)
2807 for (i = 0; i < media_mapping->len; i++) {
2808 struct media_payload_map_item *item;
2810 item = &g_array_index (media_mapping, struct media_payload_map_item, i);
2812 if (item->media_pt == media_pt)
2819 static struct media_payload_map_item *
2820 find_or_create_payload_map_for_media_pt (GArray * media_mapping, guint media_pt)
2822 struct media_payload_map_item new_item;
2823 struct media_payload_map_item *item;
2825 if ((item = find_payload_map_for_media_pt (media_mapping, media_pt)))
2828 media_payload_map_item_init (&new_item, media_pt);
2829 g_array_append_val (media_mapping, new_item);
2830 return &g_array_index (media_mapping, struct media_payload_map_item,
2831 media_mapping->len - 1);
2835 _pick_available_pt (GArray * media_mapping, guint * ret)
2839 for (i = 96; i <= 127; i++) {
2840 gboolean available = TRUE;
2843 for (j = 0; j < media_mapping->len; j++) {
2844 struct media_payload_map_item *item;
2846 item = &g_array_index (media_mapping, struct media_payload_map_item, j);
2848 if (item->media_pt == i || item->red_pt == i || item->rtx_pt == i
2849 || item->ulpfec_pt == i || item->red_rtx_pt == i) {
2866 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2867 GArray * media_mapping, gint clockrate, gint media_pt, gint * rtx_target_pt,
2868 GstSDPMedia * media)
2870 gboolean ret = TRUE;
2872 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2875 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2876 struct media_payload_map_item *item;
2879 item = find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
2880 if (item->red_pt == G_MAXUINT) {
2881 if (!(ret = _pick_available_pt (media_mapping, &item->red_pt)))
2885 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2887 str = g_strdup_printf ("%u", item->red_pt);
2888 gst_sdp_media_add_format (media, str);
2890 str = g_strdup_printf ("%u red/%d", item->red_pt, clockrate);
2891 gst_sdp_media_add_attribute (media, "rtpmap", str);
2894 *rtx_target_pt = item->red_pt;
2896 if (item->ulpfec_pt == G_MAXUINT) {
2897 if (!(ret = _pick_available_pt (media_mapping, &item->ulpfec_pt)))
2901 str = g_strdup_printf ("%u", item->ulpfec_pt);
2902 gst_sdp_media_add_format (media, str);
2904 str = g_strdup_printf ("%u ulpfec/%d", item->ulpfec_pt, clockrate);
2905 gst_sdp_media_add_attribute (media, "rtpmap", str);
2914 add_rtx_to_media (WebRTCTransceiver * trans, gint clockrate, gint rtx_pt,
2915 gint rtx_target_pt, guint target_ssrc, GstSDPMedia * media)
2919 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2920 if (target_ssrc != -1) {
2921 str = g_strdup_printf ("%u", target_ssrc);
2922 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2923 g_random_int (), NULL);
2927 str = g_strdup_printf ("%u", rtx_pt);
2928 gst_sdp_media_add_format (media, str);
2931 str = g_strdup_printf ("%u rtx/%d", rtx_pt, clockrate);
2932 gst_sdp_media_add_attribute (media, "rtpmap", str);
2935 str = g_strdup_printf ("%u apt=%d", rtx_pt, rtx_target_pt);
2936 gst_sdp_media_add_attribute (media, "fmtp", str);
2941 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2942 GArray * media_mapping, gint clockrate, gint media_pt, gint target_pt,
2943 guint target_ssrc, GstSDPMedia * media)
2945 gboolean ret = TRUE;
2947 if (trans->local_rtx_ssrc_map)
2948 gst_structure_free (trans->local_rtx_ssrc_map);
2950 trans->local_rtx_ssrc_map =
2951 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2953 if (trans->do_nack) {
2954 struct media_payload_map_item *item;
2956 item = find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
2957 if (item->rtx_pt == G_MAXUINT) {
2958 if (!(ret = _pick_available_pt (media_mapping, &item->rtx_pt)))
2962 add_rtx_to_media (trans, clockrate, item->rtx_pt, media_pt, target_ssrc,
2965 if (item->red_pt != G_MAXUINT) {
2966 /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2967 if (item->red_rtx_pt == G_MAXUINT) {
2968 if (!(ret = _pick_available_pt (media_mapping, &item->red_rtx_pt)))
2971 add_rtx_to_media (trans, clockrate, item->red_rtx_pt, item->red_pt,
2972 target_ssrc, media);
2980 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2982 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2983 GstSDPMedia * media)
2988 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2989 g_value_get_uint (value));
2990 gst_sdp_media_add_attribute (media, "ssrc-group", str);
3000 GstWebRTCBin *webrtc;
3001 WebRTCTransceiver *trans;
3005 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
3010 GstWebRTCBinPad *sink_pad;
3011 const char *msid = NULL;
3013 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
3014 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
3015 cname = gst_structure_get_string (sdes, "cname");
3018 _find_pad_for_transceiver (data->webrtc, GST_PAD_SINK,
3019 GST_WEBRTC_RTP_TRANSCEIVER (data->trans));
3021 msid = sink_pad->msid;
3022 /* fallback to cname if no msid provided */
3026 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
3027 /* FIXME: the ssrc is not present in RFC8830, do we still need that? */
3029 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
3030 msid, GST_OBJECT_NAME (data->trans));
3031 gst_sdp_media_add_attribute (data->media, "ssrc", str);
3034 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
3035 gst_sdp_media_add_attribute (data->media, "ssrc", str);
3038 gst_clear_object (&sink_pad);
3039 gst_structure_free (sdes);
3045 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
3046 WebRTCTransceiver * trans)
3049 RtxSsrcData data = { media, webrtc, trans };
3053 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
3054 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
3055 cname = gst_structure_get_string (sdes, "cname");
3057 if (trans->local_rtx_ssrc_map)
3058 gst_structure_foreach (trans->local_rtx_ssrc_map,
3059 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
3061 for (i = 0; i < gst_caps_get_size (caps); i++) {
3062 const GstStructure *s = gst_caps_get_structure (caps, i);
3065 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
3067 GstWebRTCBinPad *sink_pad;
3068 const char *msid = NULL;
3071 _find_pad_for_transceiver (webrtc, GST_PAD_SINK,
3072 GST_WEBRTC_RTP_TRANSCEIVER (trans));
3074 msid = sink_pad->msid;
3075 /* fallback to cname if no msid provided */
3079 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
3080 /* FIXME: the ssrc is not present in RFC8830, do we still need that? */
3082 g_strdup_printf ("%u msid:%s %s", ssrc, msid,
3083 GST_OBJECT_NAME (trans));
3084 gst_sdp_media_add_attribute (media, "ssrc", str);
3087 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
3088 gst_sdp_media_add_attribute (media, "ssrc", str);
3091 gst_clear_object (&sink_pad);
3095 gst_structure_free (sdes);
3097 if (trans->local_rtx_ssrc_map)
3098 gst_structure_foreach (trans->local_rtx_ssrc_map,
3099 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
3103 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
3104 GstSDPMedia * media)
3106 gchar *cert, *fingerprint, *val;
3108 g_object_get (transport, "certificate", &cert, NULL);
3111 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
3114 g_strdup_printf ("%s %s",
3115 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
3116 g_free (fingerprint);
3118 gst_sdp_media_add_attribute (media, "fingerprint", val);
3123 _parse_extmap (GQuark field_id, const GValue * value, GError ** error)
3127 if (G_VALUE_HOLDS_STRING (value)) {
3128 ret = g_value_dup_string (value);
3129 } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
3130 && gst_value_array_get_size (value) == 3) {
3132 const gchar *direction, *extensionname, *extensionattributes;
3134 val = gst_value_array_get_value (value, 0);
3135 direction = g_value_get_string (val);
3137 val = gst_value_array_get_value (value, 1);
3138 extensionname = g_value_get_string (val);
3140 val = gst_value_array_get_value (value, 2);
3141 extensionattributes = g_value_get_string (val);
3143 if (!extensionname || *extensionname == '\0')
3146 if (direction && *direction != '\0' && extensionattributes
3147 && *extensionattributes != '\0') {
3149 g_strdup_printf ("/%s %s %s", direction, extensionname,
3150 extensionattributes);
3151 } else if (direction && *direction != '\0') {
3152 ret = g_strdup_printf ("/%s %s", direction, extensionname);
3153 } else if (extensionattributes && *extensionattributes != '\0') {
3154 ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
3156 ret = g_strdup (extensionname);
3160 if (!ret && error) {
3161 gchar *val_str = gst_value_serialize (value);
3163 g_set_error (error, GST_WEBRTC_ERROR,
3164 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3165 "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
3176 GstStructure *extmap;
3181 _dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
3183 gboolean is_extmap =
3184 g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
3190 gchar *new_value = _parse_extmap (field_id, value, data->error);
3197 if (gst_structure_id_has_field (data->extmap, field_id)) {
3199 _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
3202 g_assert (old_value);
3204 if (g_strcmp0 (new_value, old_value)) {
3206 ("extmap contains different values for id %s (%s != %s)",
3207 g_quark_to_string (field_id), old_value, new_value);
3208 g_set_error (data->error, GST_WEBRTC_ERROR,
3209 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3210 "extmap contains different values for id %s (%s != %s)",
3211 g_quark_to_string (field_id), old_value, new_value);
3220 gst_structure_id_set_value (data->extmap, field_id, value);
3230 static GstStructure *
3231 _gather_extmap (GstCaps * caps, GError ** error)
3234 { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
3237 n = gst_caps_get_size (caps);
3239 for (i = 0; i < n; i++) {
3240 GstStructure *s = gst_caps_get_structure (caps, i);
3242 gst_structure_filter_and_map_in_place (s,
3243 (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
3246 gst_clear_structure (&edata.extmap);
3251 return edata.extmap;
3256 const char *rtphdrext_uri;
3261 structure_value_get_rtphdrext_id (GQuark field_id, const GValue * value,
3264 struct hdrext_id *rtphdrext = user_data;
3265 const char *field_name = g_quark_to_string (field_id);
3267 if (g_str_has_prefix (field_name, "extmap-")) {
3268 const char *val = NULL;
3270 if (GST_VALUE_HOLDS_ARRAY (value) && gst_value_array_get_size (value) >= 2) {
3271 value = gst_value_array_get_value (value, 1);
3273 if (G_VALUE_HOLDS_STRING (value)) {
3274 val = g_value_get_string (value);
3277 if (g_strcmp0 (val, rtphdrext->rtphdrext_uri) == 0) {
3278 gint64 id = g_ascii_strtoll (&field_name[strlen ("extmap-")], NULL, 10);
3280 if (id > 0 && id < 256)
3281 rtphdrext->ext_id = id;
3290 // Returns -1 when not found
3292 caps_get_rtp_header_extension_id (const GstCaps * caps,
3293 const char *rtphdrext_uri)
3297 n = gst_caps_get_size (caps);
3298 for (i = 0; i < n; i++) {
3299 const GstStructure *s = gst_caps_get_structure (caps, i);
3300 struct hdrext_id data = { rtphdrext_uri, -1 };
3302 gst_structure_foreach (s, structure_value_get_rtphdrext_id, &data);
3304 if (data.ext_id != -1)
3312 caps_contain_rtp_header_extension (const GstCaps * caps,
3313 const char *rtphdrext_uri)
3315 return caps_get_rtp_header_extension_id (caps, rtphdrext_uri) != -1;
3319 _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
3321 gst_structure_id_set_value (s, field_id, value);
3326 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
3328 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
3329 const GstSDPMedia * last_media, GstWebRTCRTPTransceiver * trans,
3330 guint media_idx, GString * bundled_mids, guint bundle_idx,
3331 gchar * bundle_ufrag, gchar * bundle_pwd, GArray * media_mapping,
3332 GHashTable * all_mids, gboolean * no_more_mlines, GError ** error)
3335 * rtp header extensions
3342 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
3344 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3345 gchar *ufrag, *pwd, *mid = NULL;
3346 gboolean bundle_only;
3347 guint rtp_session_idx;
3349 GstStructure *extmap;
3352 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3355 g_assert (trans->mline == -1 || trans->mline == media_idx);
3357 rtp_session_idx = bundled_mids ? bundle_idx : media_idx;
3359 bundle_only = bundled_mids && bundle_idx != media_idx
3360 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
3362 caps = _find_codec_preferences (webrtc, trans, media_idx, error);
3363 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3366 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
3367 gst_clear_caps (&caps);
3372 n = gst_sdp_media_formats_len (last_media);
3374 caps = gst_caps_new_empty ();
3375 for (i = 0; i < n; i++) {
3376 guint fmt = atoi (gst_sdp_media_get_format (last_media, i));
3377 GstCaps *tmp = gst_sdp_media_get_caps_from_media (last_media, fmt);
3378 GstStructure *s = gst_caps_get_structure (tmp, 0);
3379 gst_structure_set_name (s, "application/x-rtp");
3380 gst_caps_append_structure (caps, gst_structure_copy (s));
3381 gst_clear_caps (&tmp);
3383 GST_DEBUG_OBJECT (webrtc, "using previously negotiated caps for "
3384 "transceiver %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, trans, caps);
3389 if (WEBRTC_TRANSCEIVER (trans)->mline_locked) {
3390 GST_WARNING_OBJECT (webrtc,
3391 "Transceiver <%s> with mid %s has locked mline %u, but no caps. "
3392 "Can't add more lines after this one.", GST_OBJECT_NAME (trans),
3393 trans->mid, trans->mline);
3394 *no_more_mlines = TRUE;
3396 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver %"
3397 GST_PTR_FORMAT ", skipping", trans);
3404 const char *setup = gst_sdp_media_get_attribute_val (last_media, "setup");
3406 gst_sdp_media_add_attribute (media, "setup", setup);
3408 g_set_error (error, GST_WEBRTC_ERROR,
3409 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3410 "media %u cannot renegotiate without an existing a=setup line",
3415 /* mandated by JSEP */
3416 gst_sdp_media_add_attribute (media, "setup", "actpass");
3419 /* FIXME: deal with ICE restarts */
3420 if (last_offer && trans->mline != -1 && trans->mid) {
3421 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
3422 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
3423 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
3425 GST_DEBUG_OBJECT (trans,
3426 "%u Generating new ice parameters mline %i, mid %s", media_idx,
3427 trans->mline, trans->mid);
3428 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3429 _generate_ice_credentials (&ufrag, &pwd);
3431 g_assert (bundle_ufrag && bundle_pwd);
3432 ufrag = g_strdup (bundle_ufrag);
3433 pwd = g_strdup (bundle_pwd);
3437 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3438 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3442 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
3443 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3444 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3447 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3450 /* FIXME: negotiate this */
3451 /* FIXME: when bundle_only, these should not be added:
3452 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
3453 * However, this causes incompatibilities with current versions
3454 * of the major browsers */
3455 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
3456 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
3458 gst_sdp_media_add_attribute (media,
3459 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction), "");
3461 caps = gst_caps_make_writable (caps);
3463 /* When an extmap is defined twice for the same ID, firefox complains and
3464 * errors out (chrome is smart enough to accept strict duplicates).
3466 * To work around this, we deduplicate extmap attributes, and also error
3467 * out when a different extmap is defined for the same ID.
3469 * _gather_extmap will strip out all extmap- fields, which will then be
3470 * added upon adding the first format for the media.
3472 extmap = _gather_extmap (caps, error);
3475 GST_ERROR_OBJECT (webrtc,
3476 "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
3477 gst_caps_unref (caps);
3481 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3484 for (i = 0; i < gst_caps_get_size (caps); i++) {
3485 GstCaps *format = gst_caps_new_empty ();
3486 GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
3489 gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
3492 gst_caps_append_structure (format, s);
3494 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
3495 " to %u-th media", i, format, media_idx);
3497 /* this only looks at the first structure so we loop over the given caps
3498 * and add each structure inside it piecemeal */
3499 if (gst_sdp_media_set_media_from_caps (format, media) != GST_SDP_OK) {
3500 GST_ERROR_OBJECT (webrtc,
3501 "Failed to build media from caps %" GST_PTR_FORMAT
3502 " for transceiver %" GST_PTR_FORMAT, format, trans);
3503 gst_caps_unref (caps);
3504 gst_caps_unref (format);
3505 gst_structure_free (extmap);
3509 gst_caps_unref (format);
3512 gst_clear_structure (&extmap);
3515 const GstStructure *s = gst_caps_get_structure (caps, 0);
3516 gint clockrate = -1;
3518 guint rtx_target_ssrc = -1;
3521 if (gst_structure_get_int (s, "payload", &media_pt) &&
3522 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3523 find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
3525 rtx_target_pt = media_pt;
3527 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
3528 GST_WARNING_OBJECT (webrtc,
3529 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
3530 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc)) {
3531 if (!caps_contain_rtp_header_extension (caps, RTPHDREXT_MID)) {
3532 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
3537 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), media_mapping,
3538 clockrate, media_pt, &rtx_target_pt, media);
3539 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), media_mapping,
3540 clockrate, media_pt, rtx_target_pt, rtx_target_ssrc, media);
3543 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
3545 /* Some identifier; we also add the media name to it so it's identifiable */
3547 const char *media_mid = gst_sdp_media_get_attribute_val (media, "mid");
3550 gst_sdp_media_add_attribute (media, "mid", trans->mid);
3551 } else if (g_strcmp0 (media_mid, trans->mid) != 0) {
3552 g_set_error (error, GST_WEBRTC_ERROR,
3553 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3554 "Cannot change media %u mid value from \'%s\' to \'%s\'",
3555 media_idx, media_mid, trans->mid);
3558 mid = g_strdup (trans->mid);
3559 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3563 const GstStructure *s = gst_caps_get_structure (caps, 0);
3565 mid = g_strdup (gst_structure_get_string (s, "a-mid"));
3567 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3568 g_set_error (error, GST_WEBRTC_ERROR,
3569 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3570 "Cannot re-use mid \'%s\' from the caps in m= line %u that has "
3571 "already been used for a previous m= line in the SDP", mid,
3575 g_free (WEBRTC_TRANSCEIVER (trans)->pending_mid);
3576 WEBRTC_TRANSCEIVER (trans)->pending_mid = g_strdup (mid);
3577 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3582 mid = g_strdup (WEBRTC_TRANSCEIVER (trans)->pending_mid);
3584 /* If it's already used, just ignore the pending one and generate
3586 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3587 g_clear_pointer (&mid, free);
3588 g_clear_pointer (&WEBRTC_TRANSCEIVER (trans)->pending_mid, free);
3590 gst_sdp_media_add_attribute (media, "mid", mid);
3591 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3597 /* Make sure to avoid mid collisions */
3599 mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3600 webrtc->priv->media_counter++);
3601 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3604 gst_sdp_media_add_attribute (media, "mid", mid);
3605 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3606 WEBRTC_TRANSCEIVER (trans)->pending_mid = g_strdup (mid);
3613 * - add a=candidate lines for gathered candidates
3616 if (trans->sender) {
3617 if (!trans->sender->transport) {
3618 TransportStream *item;
3620 item = _get_or_create_transport_stream (webrtc, rtp_session_idx, FALSE);
3622 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
3625 _add_fingerprint_to_media (trans->sender->transport, media);
3630 g_string_append_printf (bundled_mids, " %s", mid);
3633 g_clear_pointer (&mid, g_free);
3635 gst_caps_unref (caps);
3641 gather_pad_pt (GstWebRTCBinPad * pad, GArray * media_mapping)
3643 if (pad->received_caps) {
3644 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
3647 if (gst_structure_get_int (s, "payload", &pt)) {
3648 GST_TRACE_OBJECT (pad, "have media pt %u from received caps", pt);
3649 find_or_create_payload_map_for_media_pt (media_mapping, pt);
3655 gather_media_mapping (GstWebRTCBin * webrtc)
3657 GstElement *element = GST_ELEMENT (webrtc);
3658 GArray *media_mapping =
3659 g_array_new (FALSE, FALSE, sizeof (struct media_payload_map_item));
3662 GST_OBJECT_LOCK (webrtc);
3663 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, media_mapping);
3664 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
3667 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3668 GstWebRTCRTPTransceiver *trans;
3670 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3671 GST_OBJECT_LOCK (trans);
3672 if (trans->codec_preferences) {
3676 n = gst_caps_get_size (trans->codec_preferences);
3677 for (j = 0; j < n; j++) {
3678 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
3679 if (gst_structure_get_int (s, "payload", &pt)) {
3680 GST_TRACE_OBJECT (trans, "have media pt %u from codec preferences",
3682 find_or_create_payload_map_for_media_pt (media_mapping, pt);
3686 GST_OBJECT_UNLOCK (trans);
3688 GST_OBJECT_UNLOCK (webrtc);
3690 return media_mapping;
3694 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
3695 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
3696 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
3698 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3699 gchar *ufrag, *pwd, *sdp_mid;
3700 gboolean bundle_only = bundled_mids
3701 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
3702 && gst_sdp_message_medias_len (msg) != bundle_idx;
3703 guint last_data_index = G_MAXUINT;
3705 /* add data channel support */
3706 if (webrtc->priv->data_channels->len == 0)
3710 last_data_index = _message_get_datachannel_index (last_offer);
3711 if (last_data_index < G_MAXUINT) {
3712 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
3713 /* XXX: is this always true when recycling transceivers?
3714 * i.e. do we always put the data channel in the same mline */
3715 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
3719 /* mandated by JSEP */
3720 gst_sdp_media_add_attribute (media, "setup", "actpass");
3722 /* FIXME: only needed when restarting ICE */
3723 if (last_offer && last_data_index < G_MAXUINT) {
3724 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
3725 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
3727 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3728 _generate_ice_credentials (&ufrag, &pwd);
3730 ufrag = g_strdup (bundle_ufrag);
3731 pwd = g_strdup (bundle_pwd);
3734 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3735 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3739 gst_sdp_media_set_media (media, "application");
3740 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
3741 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3742 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3743 gst_sdp_media_add_format (media, "webrtc-datachannel");
3745 if (bundle_idx != gst_sdp_message_medias_len (msg))
3746 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3748 if (last_offer && last_data_index < G_MAXUINT) {
3749 const GstSDPMedia *last_data_media;
3752 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
3753 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
3755 gst_sdp_media_add_attribute (media, "mid", mid);
3757 /* Make sure to avoid mid collisions */
3759 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3760 webrtc->priv->media_counter++);
3761 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3764 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3765 g_hash_table_insert (all_mids, sdp_mid, NULL);
3772 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3775 g_string_append_printf (bundled_mids, " %s", mid);
3778 /* FIXME: negotiate this properly */
3779 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3781 _get_or_create_data_channel_transports (webrtc,
3782 bundled_mids ? 0 : webrtc->priv->transceivers->len);
3783 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
3788 /* TODO: use the options argument */
3789 static GstSDPMessage *
3790 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3793 GstSDPMessage *ret = NULL;
3794 GString *bundled_mids = NULL;
3795 gchar *bundle_ufrag = NULL;
3796 gchar *bundle_pwd = NULL;
3797 GArray *media_mapping = NULL;
3798 GHashTable *all_mids =
3799 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3801 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3802 GList *seen_transceivers = NULL;
3803 guint media_idx = 0;
3805 gboolean no_more_mlines = FALSE;
3807 gst_sdp_message_new (&ret);
3809 gst_sdp_message_set_version (ret, "0");
3812 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
3814 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
3815 sess_id = g_strdup (origin->sess_id);
3817 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
3819 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
3823 gst_sdp_message_set_session_name (ret, "-");
3824 gst_sdp_message_add_time (ret, "0", "0", NULL);
3825 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
3827 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
3828 bundled_mids = g_string_new ("BUNDLE");
3829 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
3830 bundled_mids = g_string_new ("BUNDLE");
3833 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3834 GStrv last_bundle = NULL;
3835 guint bundle_media_index;
3837 media_mapping = gather_media_mapping (webrtc);
3838 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
3839 && last_bundle && last_bundle[0]
3840 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
3842 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
3844 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
3846 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3849 g_strfreev (last_bundle);
3852 /* FIXME: recycle transceivers */
3854 /* Fill up the renegotiated streams first */
3856 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
3857 GstWebRTCRTPTransceiver *trans = NULL;
3858 const GstSDPMedia *last_media;
3860 last_media = gst_sdp_message_get_media (last_offer, i);
3862 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
3863 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
3864 const gchar *last_mid;
3867 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
3869 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3870 WebRTCTransceiver *wtrans;
3873 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3874 wtrans = WEBRTC_TRANSCEIVER (trans);
3879 mid = wtrans->pending_mid;
3881 if (mid && g_strcmp0 (mid, last_mid) == 0) {
3884 memset (&media, 0, sizeof (media));
3886 g_assert (!g_list_find (seen_transceivers, trans));
3888 if (wtrans->mline_locked && trans->mline != media_idx) {
3889 g_set_error (error, GST_WEBRTC_ERROR,
3890 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3891 "Previous negotiatied transceiver <%s> with mid %s was in "
3892 "mline %d but transceiver has locked mline %u",
3893 GST_OBJECT_NAME (trans), trans->mid, media_idx, trans->mline);
3897 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3898 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3899 trans->mid, media_idx);
3901 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3903 g_array_new (FALSE, FALSE,
3904 sizeof (struct media_payload_map_item));
3907 gst_sdp_media_init (&media);
3908 if (!sdp_media_from_transceiver (webrtc, &media, last_media, trans,
3909 media_idx, bundled_mids, 0, bundle_ufrag, bundle_pwd,
3910 media_mapping, all_mids, &no_more_mlines, error)) {
3911 gst_sdp_media_uninit (&media);
3913 g_set_error_literal (error, GST_WEBRTC_ERROR,
3914 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3915 "Could not reuse transceiver");
3918 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3919 g_array_free (media_mapping, TRUE);
3920 media_mapping = NULL;
3925 mid = gst_sdp_media_get_attribute_val (&media, "mid");
3926 g_assert (mid && g_strcmp0 (last_mid, mid) == 0);
3928 gst_sdp_message_add_media (ret, &media);
3931 gst_sdp_media_uninit (&media);
3932 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3936 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3937 "application") == 0) {
3938 GstSDPMedia media = { 0, };
3939 gst_sdp_media_init (&media);
3940 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3941 bundle_ufrag, bundle_pwd, all_mids)) {
3942 gst_sdp_message_add_media (ret, &media);
3945 gst_sdp_media_uninit (&media);
3951 /* First, go over all transceivers and gather existing mids */
3952 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3953 GstWebRTCRTPTransceiver *trans;
3955 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3957 if (g_list_find (seen_transceivers, trans))
3961 if (g_hash_table_contains (all_mids, trans->mid)) {
3962 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3963 "Duplicate mid %s when creating offer", trans->mid);
3967 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3968 } else if (WEBRTC_TRANSCEIVER (trans)->pending_mid &&
3969 !g_hash_table_contains (all_mids,
3970 WEBRTC_TRANSCEIVER (trans)->pending_mid)) {
3971 g_hash_table_insert (all_mids,
3972 g_strdup (WEBRTC_TRANSCEIVER (trans)->pending_mid), NULL);
3977 /* add any extra streams */
3979 GstWebRTCRTPTransceiver *trans = NULL;
3980 GstSDPMedia media = { 0, };
3982 /* First find a transceiver requesting this m-line */
3983 trans = _find_transceiver_for_mline (webrtc, media_idx);
3986 /* We can't have seen it already, because it is locked to this line,
3987 * unless it's a no-more-mlines case
3989 if (!g_list_find (seen_transceivers, trans))
3990 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3992 /* Otherwise find a free transceiver */
3993 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3994 WebRTCTransceiver *wtrans;
3996 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3997 wtrans = WEBRTC_TRANSCEIVER (trans);
3999 /* don't add transceivers twice */
4000 if (g_list_find (seen_transceivers, trans))
4003 /* Ignore transceivers with a locked mline, as they would have been
4004 * found above or will be used later */
4005 if (wtrans->mline_locked)
4008 seen_transceivers = g_list_prepend (seen_transceivers, trans);
4009 /* don't add stopped transceivers */
4010 if (trans->stopped) {
4014 /* Otherwise take it */
4018 /* Stop if we got all transceivers */
4019 if (i == webrtc->priv->transceivers->len) {
4021 /* But try to add a data channel first, we do it here, because
4022 * it can allow a locked m-line to be put after, so we need to
4023 * do another iteration after.
4025 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
4026 GstSDPMedia media = { 0, };
4027 gst_sdp_media_init (&media);
4028 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
4029 bundle_ufrag, bundle_pwd, all_mids)) {
4030 if (no_more_mlines) {
4031 g_set_error (error, GST_WEBRTC_ERROR,
4032 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4033 "Trying to add data channel but there is a"
4034 " transceiver locked to line %d which doesn't have caps",
4036 gst_sdp_media_uninit (&media);
4039 gst_sdp_message_add_media (ret, &media);
4043 gst_sdp_media_uninit (&media);
4047 /* Verify that we didn't ignore any locked m-line transceivers */
4048 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4049 WebRTCTransceiver *wtrans;
4051 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
4052 wtrans = WEBRTC_TRANSCEIVER (trans);
4053 /* don't add transceivers twice */
4054 if (g_list_find (seen_transceivers, trans))
4056 g_assert (wtrans->mline_locked);
4058 g_set_error (error, GST_WEBRTC_ERROR,
4059 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4060 "Tranceiver <%s> with mid %s has locked mline %d but the offer "
4061 "only has %u sections", GST_OBJECT_NAME (trans), trans->mid,
4062 trans->mline, media_idx);
4069 if (no_more_mlines) {
4070 g_set_error (error, GST_WEBRTC_ERROR,
4071 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4072 "Trying to add transceiver at line %u but there is a transceiver "
4073 "with a locked mline for this line which doesn't have caps",
4078 gst_sdp_media_init (&media);
4080 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
4082 g_array_new (FALSE, FALSE, sizeof (struct media_payload_map_item));
4085 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
4086 "index %u", trans, media_idx);
4088 if (sdp_media_from_transceiver (webrtc, &media, NULL, trans, media_idx,
4089 bundled_mids, 0, bundle_ufrag, bundle_pwd, media_mapping, all_mids,
4090 &no_more_mlines, error)) {
4091 /* as per JSEP, a=rtcp-mux-only is only added for new streams */
4092 gst_sdp_media_add_attribute (&media, "rtcp-mux-only", "");
4093 gst_sdp_message_add_media (ret, &media);
4096 gst_sdp_media_uninit (&media);
4099 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
4100 g_array_free (media_mapping, TRUE);
4101 media_mapping = NULL;
4107 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
4108 g_array_free (media_mapping, TRUE);
4109 media_mapping = NULL;
4112 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
4115 g_assert (media_idx == gst_sdp_message_medias_len (ret));
4118 gchar *mids = g_string_free (bundled_mids, FALSE);
4120 gst_sdp_message_add_attribute (ret, "group", mids);
4122 bundled_mids = NULL;
4125 /* FIXME: pre-emptively setup receiving elements when needed */
4127 if (webrtc->priv->last_generated_answer)
4128 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4129 webrtc->priv->last_generated_answer = NULL;
4130 if (webrtc->priv->last_generated_offer)
4131 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4133 GstSDPMessage *copy;
4134 gst_sdp_message_copy (ret, ©);
4135 webrtc->priv->last_generated_offer =
4136 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
4141 g_array_free (media_mapping, TRUE);
4143 g_hash_table_unref (all_mids);
4145 g_list_free (seen_transceivers);
4148 g_free (bundle_ufrag);
4151 g_free (bundle_pwd);
4154 g_string_free (bundled_mids, TRUE);
4159 gst_sdp_message_free (ret);
4165 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
4166 gint * rtx_target_pt)
4170 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
4173 for (i = 0; i < gst_caps_get_size (caps); i++) {
4174 const GstStructure *s = gst_caps_get_structure (caps, i);
4176 if (gst_structure_has_name (s, "application/x-rtp")) {
4177 const gchar *encoding_name =
4178 gst_structure_get_string (s, "encoding-name");
4182 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
4183 gst_structure_get_int (s, "payload", &pt)) {
4184 if (!g_strcmp0 (encoding_name, "RED")) {
4187 str = g_strdup_printf ("%u", pt);
4188 gst_sdp_media_add_format (media, str);
4190 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
4191 *rtx_target_pt = pt;
4192 gst_sdp_media_add_attribute (media, "rtpmap", str);
4194 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
4197 str = g_strdup_printf ("%u", pt);
4198 gst_sdp_media_add_format (media, str);
4200 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
4201 gst_sdp_media_add_attribute (media, "rtpmap", str);
4210 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
4211 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
4214 const GstStructure *s;
4216 if (trans->local_rtx_ssrc_map)
4217 gst_structure_free (trans->local_rtx_ssrc_map);
4219 trans->local_rtx_ssrc_map =
4220 gst_structure_new_empty ("application/x-rtp-ssrc-map");
4222 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
4223 s = gst_caps_get_structure (offer_caps, i);
4225 if (gst_structure_has_name (s, "application/x-rtp")) {
4226 const gchar *encoding_name =
4227 gst_structure_get_string (s, "encoding-name");
4228 const gchar *apt_str = gst_structure_get_string (s, "apt");
4236 apt = atoi (apt_str);
4238 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
4239 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
4240 if (!g_strcmp0 (encoding_name, "RTX")) {
4243 str = g_strdup_printf ("%u", pt);
4244 gst_sdp_media_add_format (media, str);
4246 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
4247 gst_sdp_media_add_attribute (media, "rtpmap", str);
4250 str = g_strdup_printf ("%d apt=%d", pt, apt);
4251 gst_sdp_media_add_attribute (media, "fmtp", str);
4254 str = g_strdup_printf ("%u", target_ssrc);
4255 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
4256 g_random_int (), NULL);
4265 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
4266 const GstCaps * caps)
4268 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
4270 if (trans->kind == kind)
4273 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
4282 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
4283 guint * target_ssrc)
4285 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
4287 gst_structure_get_int (s, "payload", target_pt);
4288 gst_structure_get_uint (s, "ssrc", target_ssrc);
4291 /* TODO: use the options argument */
4292 static GstSDPMessage *
4293 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
4296 GstSDPMessage *ret = NULL;
4297 const GstWebRTCSessionDescription *pending_remote =
4298 webrtc->pending_remote_description;
4300 GStrv bundled = NULL;
4301 guint bundle_idx = 0;
4302 GString *bundled_mids = NULL;
4303 gchar *bundle_ufrag = NULL;
4304 gchar *bundle_pwd = NULL;
4305 GList *seen_transceivers = NULL;
4306 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
4308 if (!webrtc->pending_remote_description) {
4309 g_set_error_literal (error, GST_WEBRTC_ERROR,
4310 GST_WEBRTC_ERROR_INVALID_STATE,
4311 "Asked to create an answer without a remote description");
4315 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
4319 GStrv last_bundle = NULL;
4320 guint bundle_media_index;
4322 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
4323 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
4324 "Bundle tag is %s but no media found matching", bundled[0]);
4328 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
4329 bundled_mids = g_string_new ("BUNDLE");
4332 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
4333 && last_bundle && last_bundle[0]
4334 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
4336 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
4338 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
4340 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
4343 g_strfreev (last_bundle);
4346 gst_sdp_message_new (&ret);
4348 gst_sdp_message_set_version (ret, "0");
4350 const GstSDPOrigin *offer_origin =
4351 gst_sdp_message_get_origin (pending_remote->sdp);
4352 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
4353 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
4355 gst_sdp_message_set_session_name (ret, "-");
4357 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
4358 const GstSDPAttribute *attr =
4359 gst_sdp_message_get_attribute (pending_remote->sdp, i);
4361 if (g_strcmp0 (attr->key, "ice-options") == 0) {
4362 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
4366 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
4367 GstSDPMedia *media = NULL;
4368 GstSDPMedia *offer_media;
4369 GstWebRTCDTLSSetup offer_setup, answer_setup;
4371 gboolean bundle_only;
4375 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
4376 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
4378 gst_sdp_media_new (&media);
4379 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
4380 gst_sdp_media_set_port_info (media, 0, 0);
4382 gst_sdp_media_set_port_info (media, 9, 0);
4383 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
4388 /* FIXME: deal with ICE restarts */
4389 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
4390 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
4391 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
4394 _generate_ice_credentials (&ufrag, &pwd);
4396 ufrag = g_strdup (bundle_ufrag);
4397 pwd = g_strdup (bundle_pwd);
4400 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
4401 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
4406 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
4407 const GstSDPAttribute *attr =
4408 gst_sdp_media_get_attribute (offer_media, j);
4410 if (g_strcmp0 (attr->key, "mid") == 0
4411 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
4412 gst_sdp_media_add_attribute (media, attr->key, attr->value);
4413 /* FIXME: handle anything we want to keep */
4417 mid = gst_sdp_media_get_attribute_val (media, "mid");
4418 /* XXX: not strictly required but a lot of functionality requires a mid */
4421 /* set the a=setup: attribute */
4422 offer_setup = _get_dtls_setup_from_media (offer_media);
4423 answer_setup = _intersect_dtls_setup (offer_setup);
4424 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4425 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
4426 "transceiver direction");
4429 _media_replace_setup (media, answer_setup);
4431 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
4434 if (gst_sdp_media_formats_len (offer_media) != 1) {
4435 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
4436 "for webrtc-datachannel");
4439 sctp_port = _get_sctp_port_from_media (offer_media);
4440 if (sctp_port == -1) {
4441 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
4445 /* XXX: older browsers will produce a different SDP format for data
4446 * channel that is currently not parsed correctly */
4447 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
4449 gst_sdp_media_set_media (media, "application");
4450 gst_sdp_media_set_port_info (media, 9, 0);
4451 gst_sdp_media_add_format (media, "webrtc-datachannel");
4453 /* FIXME: negotiate this properly on renegotiation */
4454 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
4456 _get_or_create_data_channel_transports (webrtc,
4457 bundled_mids ? bundle_idx : i);
4461 g_string_append_printf (bundled_mids, " %s", mid);
4464 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
4466 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
4467 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
4468 GstCaps *offer_caps, *answer_caps = NULL;
4469 GstWebRTCRTPTransceiver *rtp_trans = NULL;
4470 WebRTCTransceiver *trans = NULL;
4471 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
4472 gint target_pt = -1;
4473 gint original_target_pt = -1;
4474 guint target_ssrc = 0;
4476 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
4477 offer_caps = _rtp_caps_from_media (offer_media);
4479 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
4480 && (rtp_trans = _find_transceiver_for_mid (webrtc, mid))) {
4481 const GstSDPMedia *last_media =
4482 gst_sdp_message_get_media (last_answer, i);
4483 const gchar *last_mid =
4484 gst_sdp_media_get_attribute_val (last_media, "mid");
4485 GstCaps *current_caps;
4487 /* FIXME: assumes no shenanigans with recycling transceivers */
4488 g_assert (g_strcmp0 (mid, last_mid) == 0);
4490 current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4492 gst_caps_unref (offer_caps);
4496 current_caps = _rtp_caps_from_media (last_media);
4499 answer_caps = gst_caps_intersect (offer_caps, current_caps);
4500 if (gst_caps_is_empty (answer_caps)) {
4501 GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
4502 GST_PTR_FORMAT ") don't intersect with caps from codec"
4503 " preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
4505 gst_caps_unref (current_caps);
4506 gst_caps_unref (answer_caps);
4507 gst_caps_unref (offer_caps);
4510 gst_caps_unref (current_caps);
4513 /* XXX: In theory we're meant to use the sendrecv formats for the
4514 * inactive direction however we don't know what that may be and would
4515 * require asking outside what it expects to possibly send later */
4517 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
4518 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
4519 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
4521 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
4522 GstCaps *trans_caps;
4524 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
4526 if (g_list_find (seen_transceivers, rtp_trans)) {
4527 /* Don't double allocate a transceiver to multiple mlines */
4532 trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
4534 gst_caps_unref (offer_caps);
4538 GST_LOG_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4539 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4541 /* FIXME: technically this is a little overreaching as some fields we
4542 * we can deal with not having and/or we may have unrecognized fields
4543 * that we cannot actually support */
4545 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4546 gst_caps_unref (trans_caps);
4548 if (!gst_caps_is_empty (answer_caps)) {
4549 GST_LOG_OBJECT (webrtc,
4550 "found compatible transceiver %" GST_PTR_FORMAT
4551 " for offer media %u", rtp_trans, i);
4554 gst_caps_unref (answer_caps);
4563 answer_dir = rtp_trans->direction;
4564 g_assert (answer_caps != NULL);
4566 /* if no transceiver, then we only receive that stream and respond with
4567 * the intersection with the transceivers codec preferences caps */
4568 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
4569 GST_WARNING_OBJECT (webrtc, "did not find compatible transceiver for "
4570 "offer caps %" GST_PTR_FORMAT ", will only receive", offer_caps);
4574 GstCaps *trans_caps;
4575 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
4577 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
4578 kind = GST_WEBRTC_KIND_AUDIO;
4579 else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
4581 kind = GST_WEBRTC_KIND_VIDEO;
4583 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
4584 GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
4586 trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
4587 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4589 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
4590 " for mline %u with media kind %d", trans, i, kind);
4592 trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4594 gst_caps_unref (offer_caps);
4598 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4599 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4601 /* FIXME: technically this is a little overreaching as some fields we
4602 * we can deal with not having and/or we may have unrecognized fields
4603 * that we cannot actually support */
4605 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4606 gst_clear_caps (&trans_caps);
4608 answer_caps = gst_caps_ref (offer_caps);
4611 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4614 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
4616 if (gst_caps_is_empty (answer_caps)) {
4617 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
4618 gst_clear_caps (&answer_caps);
4619 gst_clear_caps (&offer_caps);
4623 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps)) {
4624 GstWebRTCKind caps_kind = webrtc_kind_from_caps (answer_caps);
4626 GST_WARNING_OBJECT (webrtc,
4627 "Trying to change kind of transceiver %" GST_PTR_FORMAT
4628 " at m-line %d from %s (%d) to %s (%d)", trans, rtp_trans->mline,
4629 gst_webrtc_kind_to_string (rtp_trans->kind), rtp_trans->kind,
4630 gst_webrtc_kind_to_string (caps_kind), caps_kind);
4633 answer_caps = gst_caps_make_writable (answer_caps);
4634 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
4635 GstStructure *s = gst_caps_get_structure (answer_caps, k);
4636 /* taken from the offer sdp already and already intersected above */
4637 gst_structure_remove_field (s, "a-mid");
4638 if (!trans->do_nack)
4639 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
4642 if (gst_sdp_media_set_media_from_caps (answer_caps, media) != GST_SDP_OK) {
4643 GST_WARNING_OBJECT (webrtc,
4644 "Could not build media from caps %" GST_PTR_FORMAT, answer_caps);
4645 gst_clear_caps (&answer_caps);
4646 gst_clear_caps (&offer_caps);
4650 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
4653 original_target_pt = target_pt;
4655 _media_add_fec (media, trans, offer_caps, &target_pt);
4656 if (trans->do_nack) {
4657 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
4658 if (target_pt != original_target_pt)
4659 _media_add_rtx (media, trans, offer_caps, original_target_pt,
4663 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
4664 _media_add_ssrcs (media, answer_caps, webrtc,
4665 WEBRTC_TRANSCEIVER (rtp_trans));
4667 gst_caps_unref (answer_caps);
4670 /* set the new media direction */
4671 offer_dir = _get_direction_from_media (offer_media);
4672 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
4673 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4674 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
4675 "transceiver direction");
4676 gst_caps_unref (offer_caps);
4679 _media_replace_direction (media, answer_dir);
4681 if (!trans->stream) {
4682 TransportStream *item;
4685 _get_or_create_transport_stream (webrtc,
4686 bundled_mids ? bundle_idx : i, FALSE);
4687 webrtc_transceiver_set_transport (trans, item);
4691 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
4694 g_string_append_printf (bundled_mids, " %s", mid);
4697 /* set the a=fingerprint: for this transport */
4698 _add_fingerprint_to_media (trans->stream->transport, media);
4700 gst_caps_unref (offer_caps);
4702 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
4708 if (error && *error)
4709 GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
4711 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
4712 gst_sdp_media_free (media);
4713 gst_sdp_media_copy (offer_media, &media);
4714 gst_sdp_media_set_port_info (media, 0, 0);
4715 /* Clear error here as it is not propagated to the caller and the media
4716 * is just skipped, i.e. more iterations are going to happen. */
4717 g_clear_error (error);
4719 gst_sdp_message_add_media (ret, media);
4720 gst_sdp_media_free (media);
4724 gchar *mids = g_string_free (bundled_mids, FALSE);
4726 gst_sdp_message_add_attribute (ret, "group", mids);
4731 g_free (bundle_ufrag);
4734 g_free (bundle_pwd);
4736 /* FIXME: can we add not matched transceivers? */
4738 /* XXX: only true for the initial offerer */
4739 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
4742 g_strfreev (bundled);
4744 g_list_free (seen_transceivers);
4746 if (webrtc->priv->last_generated_offer)
4747 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4748 webrtc->priv->last_generated_offer = NULL;
4749 if (webrtc->priv->last_generated_answer)
4750 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4752 GstSDPMessage *copy;
4753 gst_sdp_message_copy (ret, ©);
4754 webrtc->priv->last_generated_answer =
4755 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
4763 GstStructure *options;
4764 GstWebRTCSDPType type;
4767 static GstStructure *
4768 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
4770 GstWebRTCSessionDescription *desc = NULL;
4771 GstSDPMessage *sdp = NULL;
4772 GstStructure *s = NULL;
4773 GError *error = NULL;
4775 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
4776 gst_webrtc_sdp_type_to_string (data->type), data->options);
4778 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
4779 sdp = _create_offer_task (webrtc, data->options, &error);
4780 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
4781 sdp = _create_answer_task (webrtc, data->options, &error);
4783 g_assert_not_reached ();
4788 desc = gst_webrtc_session_description_new (data->type, sdp);
4789 s = gst_structure_new ("application/x-gst-promise",
4790 gst_webrtc_sdp_type_to_string (data->type),
4791 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
4793 g_warn_if_fail (error != NULL);
4794 GST_WARNING_OBJECT (webrtc, "returning error: %s",
4795 error ? error->message : "Unknown");
4796 s = gst_structure_new ("application/x-gst-promise",
4797 "error", G_TYPE_ERROR, error, NULL);
4798 g_clear_error (&error);
4804 gst_webrtc_session_description_free (desc);
4810 _free_create_sdp_data (struct create_sdp *data)
4813 gst_structure_free (data->options);
4818 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
4819 const GstStructure * options, GstPromise * promise)
4821 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4824 data->options = gst_structure_copy (options);
4825 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
4827 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4828 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4830 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4831 "Could not create offer. webrtcbin is closed");
4832 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4833 "error", G_TYPE_ERROR, error, NULL);
4835 gst_promise_reply (promise, s);
4837 g_clear_error (&error);
4842 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
4843 const GstStructure * options, GstPromise * promise)
4845 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4848 data->options = gst_structure_copy (options);
4849 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
4851 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4852 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4854 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4855 "Could not create answer. webrtcbin is closed.");
4856 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4857 "error", G_TYPE_ERROR, error, NULL);
4859 gst_promise_reply (promise, s);
4861 g_clear_error (&error);
4865 static GstWebRTCBinPad *
4866 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
4867 GstWebRTCRTPTransceiver * trans, guint serial, char *msid)
4869 GstWebRTCBinPad *pad;
4872 if (direction == GST_PAD_SINK) {
4873 if (serial == G_MAXUINT)
4874 serial = webrtc->priv->max_sink_pad_serial++;
4876 serial = webrtc->priv->src_pad_counter++;
4880 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
4882 pad = gst_webrtc_bin_pad_new (pad_name, direction, msid);
4885 pad->trans = gst_object_ref (trans);
4890 static GstWebRTCRTPTransceiver *
4891 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
4892 const GstSDPMessage * sdp, guint media_idx)
4894 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4895 GstWebRTCRTPTransceiver *ret = NULL;
4898 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4899 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4901 if (g_strcmp0 (attr->key, "mid") == 0) {
4902 if ((ret = _find_transceiver_for_mid (webrtc, attr->value)))
4907 ret = _find_transceiver (webrtc, &media_idx,
4908 (FindTransceiverFunc) transceiver_match_for_mline);
4911 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
4916 _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
4918 GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4919 guint ulpfec_pt = 0, red_pt = 0;
4920 GstPad *sinkpad, *srcpad, *ghost;
4923 if (trans->stream) {
4925 transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
4926 red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
4929 if (trans->ulpfecenc || trans->redenc) {
4930 g_critical ("webrtcbin: duplicate call to create a fec encoder or "
4935 GST_DEBUG_OBJECT (webrtc,
4936 "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
4939 ret = gst_bin_new (NULL);
4941 trans->ulpfecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4942 gst_object_ref_sink (trans->ulpfecenc);
4943 if (!gst_bin_add (GST_BIN (ret), trans->ulpfecenc))
4944 g_warn_if_reached ();
4945 sinkpad = gst_element_get_static_pad (trans->ulpfecenc, "sink");
4947 g_object_bind_property (rtp_trans, "fec-percentage", trans->ulpfecenc,
4948 "percentage", G_BINDING_DEFAULT);
4950 trans->redenc = gst_element_factory_make ("rtpredenc", NULL);
4951 gst_object_ref_sink (trans->redenc);
4953 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
4954 rtp_trans->mline, red_pt);
4956 gst_bin_add (GST_BIN (ret), trans->redenc);
4957 gst_element_link (trans->ulpfecenc, trans->redenc);
4959 ghost = gst_ghost_pad_new ("sink", sinkpad);
4960 gst_clear_object (&sinkpad);
4961 gst_element_add_pad (ret, ghost);
4964 srcpad = gst_element_get_static_pad (trans->redenc, "src");
4965 ghost = gst_ghost_pad_new ("src", srcpad);
4966 gst_clear_object (&srcpad);
4967 gst_element_add_pad (ret, ghost);
4974 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
4976 GstStructure *s = user_data;
4978 gst_structure_id_set_value (s, field_id, value);
4983 #define GST_WEBRTC_PAYLOAD_TYPE "gst.webrtcbin.payload.type"
4986 try_match_transceiver_with_fec_decoder (GstWebRTCBin * webrtc,
4987 WebRTCTransceiver * trans)
4991 for (l = trans->stream->fecdecs; l; l = l->next) {
4992 GstElement *fecdec = GST_ELEMENT (l->data);
4993 gboolean found_transceiver = FALSE;
4998 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fecdec),
4999 GST_WEBRTC_PAYLOAD_TYPE));
5000 if (original_pt <= 0) {
5001 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
5002 "transceiver, fec decoder %" GST_PTR_FORMAT " does not contain a "
5003 "valid payload type", fecdec);
5007 for (i = 0; i < trans->stream->ptmap->len; i++) {
5008 PtMapItem *item = &g_array_index (trans->stream->ptmap, PtMapItem, i);
5010 /* FIXME: this only works for a 1-1 original_pt->fec_pt mapping */
5011 if (original_pt == item->pt && item->media_idx != -1
5012 && item->media_idx == trans->parent.mline) {
5013 if (trans->ulpfecdec) {
5014 GST_FIXME_OBJECT (trans, "cannot");
5015 gst_clear_object (&trans->ulpfecdec);
5017 trans->ulpfecdec = gst_object_ref (fecdec);
5018 found_transceiver = TRUE;
5023 if (!found_transceiver) {
5024 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
5031 _set_internal_rtpbin_element_props_from_stream (GstWebRTCBin * webrtc,
5032 TransportStream * stream)
5034 GstStructure *merged_local_rtx_ssrc_map;
5035 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
5036 GValue red_pt_array = { 0, };
5041 gst_value_array_init (&red_pt_array, 0);
5043 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
5044 GST_DEBUG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
5046 for (i = 0; i < rtx_count; i++) {
5047 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
5048 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
5049 const gchar *apt = gst_structure_get_string (s, "apt");
5051 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
5052 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
5055 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
5056 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
5057 stream->rtxsend, pt_map);
5059 if (stream->rtxreceive)
5060 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
5061 if (stream->rtxsend)
5062 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
5064 gst_structure_free (pt_map);
5065 g_clear_pointer (&rtx_pt, g_free);
5067 merged_local_rtx_ssrc_map =
5068 gst_structure_new_empty ("application/x-rtp-ssrc-map");
5070 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5071 GstWebRTCRTPTransceiver *rtp_trans =
5072 g_ptr_array_index (webrtc->priv->transceivers, i);
5073 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
5075 if (trans->stream == stream) {
5076 gint ulpfec_pt, red_pt = 0;
5078 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC", rtp_trans->mline);
5082 red_pt = transport_stream_get_pt (stream, "RED", rtp_trans->mline);
5086 GValue ptval = { 0, };
5088 g_value_init (&ptval, G_TYPE_INT);
5089 g_value_set_int (&ptval, red_pt);
5090 gst_value_array_append_value (&red_pt_array, &ptval);
5091 g_value_unset (&ptval);
5094 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " transceiver %"
5095 GST_PTR_FORMAT " has FEC payload %d and RED payload %d", stream,
5096 trans, ulpfec_pt, red_pt);
5098 if (trans->ulpfecenc) {
5099 guint ulpfecenc_pt = ulpfec_pt;
5101 if (ulpfecenc_pt == 0)
5104 g_object_set (trans->ulpfecenc, "pt", ulpfecenc_pt, "multipacket",
5105 rtp_trans->kind == GST_WEBRTC_KIND_VIDEO, "percentage",
5106 trans->fec_percentage, NULL);
5109 try_match_transceiver_with_fec_decoder (webrtc, trans);
5110 if (trans->ulpfecdec) {
5111 g_object_set (trans->ulpfecdec, "passthrough", ulpfec_pt == 0, "pt",
5115 if (trans->redenc) {
5116 gboolean always_produce = TRUE;
5118 /* passthrough settings */
5120 always_produce = FALSE;
5122 g_object_set (trans->redenc, "pt", red_pt, "allow-no-red-blocks",
5123 always_produce, NULL);
5126 if (trans->local_rtx_ssrc_map) {
5127 gst_structure_foreach (trans->local_rtx_ssrc_map,
5128 _merge_structure, merged_local_rtx_ssrc_map);
5133 if (stream->rtxsend)
5134 g_object_set (stream->rtxsend, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
5135 gst_clear_structure (&merged_local_rtx_ssrc_map);
5137 if (stream->reddec) {
5138 g_object_set_property (G_OBJECT (stream->reddec), "payloads",
5142 g_value_unset (&red_pt_array);
5146 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
5151 * ,--------------------------------------------webrtcbin--------------------------------------------,
5153 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
5154 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
5155 * ; ,---clocksync---, ; ; ; ; ;
5156 * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
5157 * ; sink_%u ; ; ,---fec encoder---, ; ; '---------------------' ;
5158 * o---------o sink src o-o sink src o--o send_rtp_sink_%u ; ;
5159 * ; '---------------' ,-----------------, '--------------------' ;
5160 * '-------------------------------------------------------------------------------------------------'
5165 * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
5167 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
5168 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
5170 * ; sink_%u ,---clocksync---, ,---fec encoder---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
5171 * o----------o sink src o-o sink src o--o sink_%u ; ; ; '---------------------' ;
5172 * ; '---------------' ,-----------------, ; ; ; ; ;
5173 * ; ; src o-o send_rtp_sink_%u ; ;
5174 * ; sink_%u ,---clocksync---, ,---fec encoder---, ; ; ; ; ;
5175 * o----------o sink src o-o sink src o--o sink%u ; '--------------------' ;
5176 * ; '---------------' ,-----------------, '------------' ;
5177 * '-----------------------------------------------------------------------------------------------------------------'
5179 GstPadTemplate *rtp_templ;
5180 GstPad *rtp_sink, *sinkpad, *srcpad;
5182 WebRTCTransceiver *trans;
5183 GstElement *clocksync;
5184 GstElement *fec_encoder;
5186 g_return_val_if_fail (pad->trans != NULL, NULL);
5188 trans = WEBRTC_TRANSCEIVER (pad->trans);
5190 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
5192 g_assert (trans->stream);
5194 clocksync = gst_element_factory_make ("clocksync", NULL);
5195 g_object_set (clocksync, "sync", TRUE, NULL);
5196 gst_bin_add (GST_BIN (webrtc), clocksync);
5197 gst_element_sync_state_with_parent (clocksync);
5199 srcpad = gst_element_get_static_pad (clocksync, "src");
5201 fec_encoder = _build_fec_encoder (webrtc, trans);
5203 g_warn_if_reached ();
5207 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
5209 gst_bin_add (GST_BIN (webrtc), fec_encoder);
5210 gst_element_sync_state_with_parent (fec_encoder);
5212 sinkpad = gst_element_get_static_pad (fec_encoder, "sink");
5213 if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)
5214 g_warn_if_reached ();
5215 gst_clear_object (&srcpad);
5216 gst_clear_object (&sinkpad);
5217 sinkpad = gst_element_get_static_pad (clocksync, "sink");
5218 srcpad = gst_element_get_static_pad (fec_encoder, "src");
5220 if (!webrtc->rtpfunnel) {
5222 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
5223 "send_rtp_sink_%u");
5224 g_assert (rtp_templ);
5226 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
5228 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
5230 gst_pad_link (srcpad, rtp_sink);
5231 gst_object_unref (rtp_sink);
5233 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
5234 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5235 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
5236 g_warn_if_reached ();
5239 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
5240 GstPad *funnel_sinkpad =
5241 gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
5243 gst_pad_link (srcpad, funnel_sinkpad);
5246 gst_object_unref (funnel_sinkpad);
5249 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
5251 gst_clear_object (&srcpad);
5252 gst_clear_object (&sinkpad);
5254 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
5256 return GST_PAD (pad);
5259 /* output pads are receiving elements */
5261 _connect_output_stream (GstWebRTCBin * webrtc,
5262 TransportStream * stream, guint session_id)
5265 * ,------------------------webrtcbin------------------------,
5266 * ; ,---------rtpbin---------, ;
5267 * ; ,-transport_receive_%u--, ; ; ;
5268 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
5270 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
5271 * ; '-----------------------' ; ; ; src_%u
5272 * ; ; recv_rtp_src_%u_%u_%u o--o
5273 * ; '------------------------' ;
5274 * '---------------------------------------------------------'
5278 if (stream->output_connected) {
5279 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
5280 "connected to rtpbin. Not connecting", stream);
5284 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
5285 session_id, stream);
5287 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
5288 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
5289 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
5290 g_warn_if_reached ();
5293 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
5295 /* The webrtcbin src_%u output pads will be created when rtpbin receives
5296 * data on that stream in on_rtpbin_pad_added() */
5298 stream->output_connected = TRUE;
5308 _clear_ice_candidate_item (IceCandidateItem * item)
5310 g_free (item->candidate);
5314 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
5315 gboolean drop_invalid)
5317 GstWebRTCICEStream *stream;
5319 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
5320 if (stream == NULL) {
5322 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
5325 IceCandidateItem new;
5326 new.mlineindex = item->mlineindex;
5327 new.candidate = g_strdup (item->candidate);
5328 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
5331 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5332 ICE_UNLOCK (webrtc);
5337 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
5338 item->mlineindex, item->candidate);
5340 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
5344 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
5345 const GstSDPMedia * media)
5348 GstWebRTCICEStream *stream = NULL;
5350 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
5351 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
5352 if (g_strcmp0 (attr->key, "candidate") == 0) {
5356 stream = _find_ice_stream_for_session (webrtc, mlineindex);
5357 if (stream == NULL) {
5358 GST_DEBUG_OBJECT (webrtc,
5359 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
5363 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
5364 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
5365 mlineindex, candidate);
5366 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
5373 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
5374 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
5376 GstSDPMedia *media = NULL;
5378 if (mline_index < sdp->medias->len) {
5379 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
5382 if (media == NULL) {
5383 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
5387 // Add the candidate as an attribute, first stripping off the existing
5388 // candidate: key from the string description
5389 if (strlen (candidate) < 10) {
5390 GST_WARNING_OBJECT (webrtc,
5391 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
5395 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
5399 _filter_sdp_fields (GQuark field_id, const GValue * value,
5400 GstStructure * new_structure)
5402 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
5403 gst_structure_id_set_value (new_structure, field_id, value);
5409 transport_stream_ptmap_get_rtp_header_extension_id (TransportStream * stream,
5410 const char *rtphdrext_uri)
5414 for (i = 0; i < stream->ptmap->len; i++) {
5415 PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
5418 id = caps_get_rtp_header_extension_id (item->caps, rtphdrext_uri);
5427 ensure_rtx_hdr_ext (TransportStream * stream)
5429 stream->rtphdrext_id_stream_id =
5430 transport_stream_ptmap_get_rtp_header_extension_id (stream,
5431 RTPHDREXT_STREAM_ID);
5432 stream->rtphdrext_id_repaired_stream_id =
5433 transport_stream_ptmap_get_rtp_header_extension_id (stream,
5434 RTPHDREXT_REPAIRED_STREAM_ID);
5436 /* TODO: removing header extensions usage from rtx on renegotiation */
5438 if (stream->rtxsend) {
5439 if (stream->rtphdrext_id_stream_id != -1 && !stream->rtxsend_stream_id) {
5440 stream->rtxsend_stream_id =
5441 gst_rtp_header_extension_create_from_uri (RTPHDREXT_STREAM_ID);
5442 if (!stream->rtxsend_stream_id)
5443 g_warn_if_reached ();
5444 gst_rtp_header_extension_set_id (stream->rtxsend_stream_id,
5445 stream->rtphdrext_id_stream_id);
5447 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5448 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_stream_id,
5449 stream->rtphdrext_id_stream_id, stream->rtxsend);
5451 g_signal_emit_by_name (stream->rtxsend, "add-extension",
5452 stream->rtxsend_stream_id);
5455 if (stream->rtphdrext_id_repaired_stream_id != -1
5456 && !stream->rtxsend_repaired_stream_id) {
5457 stream->rtxsend_repaired_stream_id =
5458 gst_rtp_header_extension_create_from_uri
5459 (RTPHDREXT_REPAIRED_STREAM_ID);
5460 if (!stream->rtxsend_repaired_stream_id)
5461 g_warn_if_reached ();
5462 gst_rtp_header_extension_set_id (stream->rtxsend_repaired_stream_id,
5463 stream->rtphdrext_id_repaired_stream_id);
5465 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5466 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_repaired_stream_id,
5467 stream->rtphdrext_id_repaired_stream_id, stream->rtxsend);
5469 g_signal_emit_by_name (stream->rtxsend, "add-extension",
5470 stream->rtxsend_repaired_stream_id);
5474 if (stream->rtxreceive) {
5475 if (stream->rtphdrext_id_stream_id != -1 && !stream->rtxreceive_stream_id) {
5476 stream->rtxreceive_stream_id =
5477 gst_rtp_header_extension_create_from_uri (RTPHDREXT_STREAM_ID);
5478 if (!stream->rtxreceive_stream_id)
5479 g_warn_if_reached ();
5480 gst_rtp_header_extension_set_id (stream->rtxreceive_stream_id,
5481 stream->rtphdrext_id_stream_id);
5483 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5484 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_stream_id,
5485 stream->rtphdrext_id_stream_id, stream->rtxreceive);
5487 g_signal_emit_by_name (stream->rtxreceive, "add-extension",
5488 stream->rtxreceive_stream_id);
5491 if (stream->rtphdrext_id_repaired_stream_id != -1
5492 && !stream->rtxreceive_repaired_stream_id) {
5493 stream->rtxreceive_repaired_stream_id =
5494 gst_rtp_header_extension_create_from_uri
5495 (RTPHDREXT_REPAIRED_STREAM_ID);
5496 if (!stream->rtxreceive_repaired_stream_id)
5497 g_warn_if_reached ();
5498 gst_rtp_header_extension_set_id (stream->rtxreceive_repaired_stream_id,
5499 stream->rtphdrext_id_repaired_stream_id);
5501 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5502 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_repaired_stream_id,
5503 stream->rtphdrext_id_repaired_stream_id, stream->rtxreceive);
5505 g_signal_emit_by_name (stream->rtxreceive, "add-extension",
5506 stream->rtxreceive_repaired_stream_id);
5512 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
5513 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
5517 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5520 proto = gst_sdp_media_get_proto (media);
5521 if (proto != NULL) {
5522 /* Parse global SDP attributes once */
5523 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
5524 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
5525 gst_sdp_message_attributes_to_caps (sdp, global_caps);
5526 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
5527 gst_sdp_media_attributes_to_caps (media, global_caps);
5529 len = gst_sdp_media_formats_len (media);
5530 for (i = 0; i < len; i++) {
5531 GstCaps *caps, *outcaps;
5537 pt = atoi (gst_sdp_media_get_format (media, i));
5539 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
5542 caps = gst_sdp_media_get_caps_from_media (media, pt);
5544 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
5548 /* Merge in global caps */
5549 /* Intersect will merge in missing fields to the current caps */
5550 outcaps = gst_caps_intersect (caps, global_caps);
5551 gst_caps_unref (caps);
5553 s = gst_caps_get_structure (outcaps, 0);
5554 gst_structure_set_name (s, "application/x-rtp");
5555 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
5556 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
5558 item.caps = gst_caps_new_empty ();
5560 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
5561 GstStructure *s = gst_caps_get_structure (outcaps, j);
5562 GstStructure *filtered =
5563 gst_structure_new_empty (gst_structure_get_name (s));
5565 gst_structure_foreach (s,
5566 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
5567 gst_caps_append_structure (item.caps, filtered);
5571 item.media_idx = media_idx;
5572 gst_caps_unref (outcaps);
5574 g_array_append_val (stream->ptmap, item);
5577 gst_caps_unref (global_caps);
5582 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
5583 const GstSDPMessage * sdp, guint media_idx,
5584 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
5585 GStrv bundled, guint bundle_idx, GError ** error)
5587 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
5588 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
5589 GstWebRTCRTPTransceiverDirection new_dir;
5590 const GstSDPMedia *local_media, *remote_media;
5591 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5592 GstWebRTCDTLSSetup new_setup;
5593 char *local_msid = NULL;
5594 gboolean new_rtcp_rsize;
5595 ReceiveState receive_state = RECEIVE_STATE_UNSET;
5599 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5602 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5605 rtp_trans->mline = media_idx;
5607 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
5608 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
5609 GST_FIXME_OBJECT (webrtc, "Updating video transceiver %" GST_PTR_FORMAT
5610 " to audio, which isn't fully supported.", rtp_trans);
5611 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
5614 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
5615 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
5616 GST_FIXME_OBJECT (webrtc, "Updating audio transceiver %" GST_PTR_FORMAT
5617 " to video, which isn't fully supported.", rtp_trans);
5618 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
5621 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
5622 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
5624 if (g_strcmp0 (attr->key, "mid") == 0) {
5625 g_free (rtp_trans->mid);
5626 rtp_trans->mid = g_strdup (attr->value);
5631 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
5632 GstWebRTCDTLSSetup local_setup, remote_setup;
5634 local_setup = _get_dtls_setup_from_media (local_media);
5635 remote_setup = _get_dtls_setup_from_media (remote_media);
5636 new_setup = _get_final_setup (local_setup, remote_setup);
5637 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5638 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5639 "Cannot intersect direction attributes for media %u", media_idx);
5643 local_dir = _get_direction_from_media (local_media);
5644 remote_dir = _get_direction_from_media (remote_media);
5645 new_dir = _get_final_direction (local_dir, remote_dir);
5646 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
5647 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5648 "Cannot intersect dtls setup attributes for media %u", media_idx);
5652 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
5653 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
5654 && prev_dir != new_dir) {
5655 g_set_error (error, GST_WEBRTC_ERROR,
5656 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5657 "transceiver direction changes are not implemented. Media %u",
5662 if (!bundled || bundle_idx == media_idx) {
5663 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
5664 && _media_has_attribute_key (remote_media, "rtcp-rsize");
5668 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
5669 media_idx, &session);
5671 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
5672 g_object_unref (session);
5678 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5680 /* Not a bundled stream means this entire transport is inactive,
5681 * so set the receive state to BLOCK below */
5682 stream->active = FALSE;
5683 receive_state = RECEIVE_STATE_BLOCK;
5686 /* If this transceiver is active for sending or receiving,
5687 * we still need receive at least RTCP, so need to unblock
5688 * the receive bin below. */
5689 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
5690 receive_state = RECEIVE_STATE_PASS;
5691 stream->active = TRUE;
5694 if (new_dir != prev_dir) {
5695 guint rtp_session_id = bundled ? bundle_idx : media_idx;
5697 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
5698 " direction change from %s to %s", rtp_trans,
5699 gst_webrtc_rtp_transceiver_direction_to_string (prev_dir),
5700 gst_webrtc_rtp_transceiver_direction_to_string (new_dir));
5702 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5703 GstWebRTCBinPad *pad;
5705 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
5707 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
5709 GstPad *peer = gst_pad_get_peer (target);
5711 gst_pad_send_event (peer, gst_event_new_eos ());
5712 gst_object_unref (peer);
5714 gst_object_unref (target);
5716 gst_object_unref (pad);
5719 /* XXX: send eos event up the sink pad as well? */
5722 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
5723 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5724 GstWebRTCBinPad *pad =
5725 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
5726 local_msid = _get_msid_from_media (local_media);
5729 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
5730 " for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans,
5732 if (g_strcmp0 (pad->msid, local_msid) != 0) {
5733 GST_DEBUG_OBJECT (webrtc, "send pad %" GST_PTR_FORMAT
5734 " transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'"
5735 " to \'%s\'", pad, trans, pad->msid, local_msid);
5736 g_clear_pointer (&pad->msid, g_free);
5737 pad->msid = local_msid;
5738 g_object_notify (G_OBJECT (pad), "msid");
5741 g_clear_pointer (&local_msid, g_free);
5743 gst_object_unref (pad);
5745 GST_DEBUG_OBJECT (webrtc,
5746 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
5747 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
5748 G_MAXUINT, local_msid);
5750 _connect_input_stream (webrtc, pad);
5751 _add_pad (webrtc, pad);
5754 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
5755 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5756 GstWebRTCBinPad *pad =
5757 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5758 char *remote_msid = _get_msid_from_media (remote_media);
5761 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
5762 " for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans,
5764 if (g_strcmp0 (pad->msid, remote_msid) != 0) {
5765 GST_DEBUG_OBJECT (webrtc, "receive pad %" GST_PTR_FORMAT
5766 " transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'"
5767 " to \'%s\'", pad, trans, pad->msid, remote_msid);
5768 g_clear_pointer (&pad->msid, g_free);
5769 pad->msid = remote_msid;
5771 g_object_notify (G_OBJECT (pad), "msid");
5773 g_clear_pointer (&remote_msid, g_free);
5775 gst_object_unref (pad);
5777 GST_DEBUG_OBJECT (webrtc,
5778 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
5779 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
5780 G_MAXUINT, remote_msid);
5783 if (!trans->stream) {
5784 TransportStream *item;
5787 _get_or_create_transport_stream (webrtc, rtp_session_id, FALSE);
5788 webrtc_transceiver_set_transport (trans, item);
5791 _connect_output_stream (webrtc, trans->stream, rtp_session_id);
5792 /* delay adding the pad until rtpbin creates the recv output pad
5793 * to ghost to so queries/events travel through the pipeline correctly
5794 * as soon as the pad is added */
5795 _add_pad_to_list (webrtc, pad);
5799 rtp_trans->mline = media_idx;
5800 rtp_trans->current_direction = new_dir;
5803 if (!bundled || bundle_idx == media_idx) {
5804 if (stream->rtxsend || stream->rtxreceive) {
5805 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
5808 g_object_set (stream, "dtls-client",
5809 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5812 /* Must be after setting the "dtls-client" so that data is not pushed into
5813 * the dtlssrtp elements before the ssl direction has been set which will
5814 * throw SSL errors */
5815 if (receive_state != RECEIVE_STATE_UNSET)
5816 transport_receive_bin_set_receive_state (stream->receive_bin,
5820 /* must be called with the pc lock held */
5822 _generate_data_channel_id (GstWebRTCBin * webrtc)
5825 gint new_id = -1, max_channels = 0;
5827 if (webrtc->priv->sctp_transport) {
5828 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5831 if (max_channels <= 0) {
5832 max_channels = 65534;
5835 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
5838 /* TODO: a better search algorithm */
5840 WebRTCDataChannel *channel;
5844 if (new_id < 0 || new_id >= max_channels) {
5845 /* exhausted id space */
5846 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
5847 "data channel id (max %i)", max_channels);
5851 /* client must generate even ids, server must generate odd ids */
5852 if (new_id % 2 == !(!is_client))
5855 channel = _find_data_channel_for_id (webrtc, new_id);
5864 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
5865 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
5868 const GstSDPMedia *local_media, *remote_media;
5869 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
5870 TransportReceiveBin *receive;
5871 int local_port, remote_port;
5872 guint64 local_max_size, remote_max_size, max_size;
5876 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5879 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5882 local_setup = _get_dtls_setup_from_media (local_media);
5883 remote_setup = _get_dtls_setup_from_media (remote_media);
5884 new_setup = _get_final_setup (local_setup, remote_setup);
5885 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5886 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5887 "Cannot intersect dtls setup for media %u", media_idx);
5891 /* data channel is always rtcp-muxed to avoid generating ICE candidates
5893 g_object_set (stream, "dtls-client",
5894 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5896 local_port = _get_sctp_port_from_media (local_media);
5897 remote_port = _get_sctp_port_from_media (local_media);
5898 if (local_port == -1 || remote_port == -1) {
5899 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5900 "Could not find sctp port for media %u (local %i, remote %i)",
5901 media_idx, local_port, remote_port);
5905 if (0 == (local_max_size =
5906 _get_sctp_max_message_size_from_media (local_media)))
5907 local_max_size = G_MAXUINT64;
5908 if (0 == (remote_max_size =
5909 _get_sctp_max_message_size_from_media (remote_media)))
5910 remote_max_size = G_MAXUINT64;
5911 max_size = MIN (local_max_size, remote_max_size);
5913 webrtc->priv->sctp_transport->max_message_size = max_size;
5916 guint orig_local_port, orig_remote_port;
5918 /* XXX: sctpassociation warns if we are in the wrong state */
5919 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5920 &orig_local_port, NULL);
5922 if (orig_local_port != local_port)
5923 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5926 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5927 &orig_remote_port, NULL);
5928 if (orig_remote_port != remote_port)
5929 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5934 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
5935 WebRTCDataChannel *channel;
5937 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
5939 if (channel->parent.id == -1)
5940 channel->parent.id = _generate_data_channel_id (webrtc);
5941 if (channel->parent.id == -1)
5942 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5943 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5945 if (webrtc->priv->sctp_transport->association_established
5946 && !channel->parent.negotiated && !channel->opened) {
5947 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
5948 webrtc_data_channel_start_negotiation (channel);
5953 stream->active = TRUE;
5955 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
5956 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
5960 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
5963 GstWebRTCKind kind = GPOINTER_TO_INT (data);
5967 if (p1->mline != -1)
5971 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
5978 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
5983 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
5987 if (webrtc->rtpfunnel)
5990 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
5991 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
5992 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
5994 srcpad = gst_element_get_static_pad (webrtc->rtpfunnel, "src");
5996 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
5997 rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
6000 gst_pad_link (srcpad, rtp_sink);
6001 gst_object_unref (srcpad);
6002 gst_object_unref (rtp_sink);
6004 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
6005 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
6006 GST_ELEMENT (stream->send_bin), "rtp_sink"))
6007 g_warn_if_reached ();
6015 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
6016 GstWebRTCSessionDescription * sdp, GError ** error)
6019 gboolean ret = FALSE;
6020 GStrv bundled = NULL;
6021 guint bundle_idx = 0;
6022 TransportStream *bundle_stream = NULL;
6024 /* FIXME: With some peers, it's possible we could have
6025 * multiple bundles to deal with, although I've never seen one yet */
6026 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
6027 if (!_parse_bundle (sdp->sdp, &bundled, error))
6032 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
6033 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6034 "Bundle tag is %s but no media found matching", bundled[0]);
6038 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
6039 _message_media_is_datachannel (sdp->sdp, bundle_idx));
6040 /* Mark the bundle stream as inactive to start. It will be set to TRUE
6041 * by any bundled mline that is active, and at the end we set the
6042 * receivebin to BLOCK if all mlines were inactive. */
6043 bundle_stream->active = FALSE;
6045 g_array_set_size (bundle_stream->ptmap, 0);
6046 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
6047 /* When bundling, we need to do this up front, or else RTX
6048 * parameters aren't set up properly for the bundled streams */
6049 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
6051 ensure_rtx_hdr_ext (bundle_stream);
6053 _connect_rtpfunnel (webrtc, bundle_idx);
6056 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
6057 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
6058 TransportStream *stream;
6059 GstWebRTCRTPTransceiver *trans;
6060 guint transport_idx;
6062 /* skip rejected media */
6063 if (gst_sdp_media_get_port (media) == 0)
6067 transport_idx = bundle_idx;
6071 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
6073 stream = _get_or_create_transport_stream (webrtc, transport_idx,
6074 _message_media_is_datachannel (sdp->sdp, transport_idx));
6076 /* When bundling, these were all set up above, but when not
6077 * bundling we need to do it now */
6078 g_array_set_size (stream->ptmap, 0);
6079 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
6080 ensure_rtx_hdr_ext (stream);
6084 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
6086 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
6087 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6088 "State mismatch. Could not find local transceiver by mline %u", i);
6091 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
6092 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
6093 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
6095 /* No existing transceiver, find an unused one */
6097 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
6098 kind = GST_WEBRTC_KIND_AUDIO;
6099 else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
6100 kind = GST_WEBRTC_KIND_VIDEO;
6102 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
6103 GST_STR_NULL (gst_sdp_media_get_media (media)));
6105 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
6106 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
6109 /* Still no transceiver? Create one */
6110 /* XXX: default to the advertised direction in the sdp for new
6111 * transceivers. The spec doesn't actually say what happens here, only
6112 * that calls to setDirection will change the value. Nothing about
6113 * a default value when the transceiver is created internally */
6115 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
6116 _get_direction_from_media (media), i, kind, NULL);
6117 webrtc_transceiver_set_transport (t, stream);
6118 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
6121 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
6122 trans, bundled, bundle_idx, error);
6123 if (error && *error)
6125 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
6126 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
6128 if (error && *error)
6131 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
6136 if (bundle_stream && bundle_stream->active == FALSE) {
6137 /* No bundled mline marked the bundle as active, so block the receive bin, as
6138 * this bundle is completely inactive */
6139 GST_LOG_OBJECT (webrtc,
6140 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
6141 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
6142 RECEIVE_STATE_BLOCK);
6148 g_strfreev (bundled);
6154 transceivers_media_num_cmp (GstWebRTCBin * webrtc,
6155 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
6160 return gst_sdp_message_medias_len (new->sdp) -
6161 gst_sdp_message_medias_len (previous->sdp);
6166 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
6171 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
6172 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
6173 GstWebRTCRTPTransceiver *rtp_trans;
6174 WebRTCTransceiver *trans;
6176 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
6177 /* only look for matching mid */
6178 if (rtp_trans == NULL)
6181 trans = WEBRTC_TRANSCEIVER (rtp_trans);
6183 /* We only validate the locked mlines for now */
6184 if (!trans->mline_locked)
6187 if (rtp_trans->mline != i) {
6188 g_set_error (error, GST_WEBRTC_ERROR,
6189 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
6190 "m-line with mid %s is at position %d, but was locked to %d, "
6191 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
6195 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
6196 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
6197 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
6198 g_set_error (error, GST_WEBRTC_ERROR,
6199 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
6200 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
6201 "%s media", i, GST_OBJECT_NAME (rtp_trans),
6202 gst_webrtc_kind_to_string (rtp_trans->kind),
6203 gst_sdp_media_get_media (media));
6207 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
6208 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
6209 g_set_error (error, GST_WEBRTC_ERROR,
6210 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
6211 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
6212 "%s media", i, GST_OBJECT_NAME (rtp_trans),
6213 gst_webrtc_kind_to_string (rtp_trans->kind),
6214 gst_sdp_media_get_media (media));
6224 struct set_description
6227 GstWebRTCSessionDescription *sdp;
6230 static GstWebRTCSessionDescription *
6231 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
6232 GstWebRTCSDPType type)
6235 case GST_WEBRTC_SDP_TYPE_OFFER:
6236 case GST_WEBRTC_SDP_TYPE_PRANSWER:
6237 case GST_WEBRTC_SDP_TYPE_ANSWER:
6238 if (source == SDP_LOCAL) {
6239 return webrtc->current_local_description;
6241 return webrtc->current_remote_description;
6243 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
6246 /* other values mean memory corruption/uninitialized! */
6247 g_assert_not_reached ();
6254 static GstWebRTCSessionDescription *
6255 get_last_generated_description (GstWebRTCBin * webrtc, SDPSource source,
6256 GstWebRTCSDPType type)
6259 case GST_WEBRTC_SDP_TYPE_OFFER:
6260 if (source == SDP_REMOTE)
6261 return webrtc->priv->last_generated_answer;
6263 return webrtc->priv->last_generated_offer;
6265 case GST_WEBRTC_SDP_TYPE_PRANSWER:
6266 case GST_WEBRTC_SDP_TYPE_ANSWER:
6267 if (source == SDP_LOCAL)
6268 return webrtc->priv->last_generated_answer;
6270 return webrtc->priv->last_generated_offer;
6271 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
6274 /* other values mean memory corruption/uninitialized! */
6275 g_assert_not_reached ();
6283 /* http://w3c.github.io/webrtc-pc/#set-description */
6284 static GstStructure *
6285 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
6287 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
6288 gboolean signalling_state_changed = FALSE;
6289 GError *error = NULL;
6290 GStrv bundled = NULL;
6291 guint bundle_idx = 0;
6295 const gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6296 webrtc->signaling_state);
6297 const gchar *type_str =
6298 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
6299 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
6300 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
6301 _sdp_source_to_string (sd->source), type_str, state);
6302 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
6306 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
6309 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
6310 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
6314 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
6315 g_set_error (&error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6316 "Bundle tag is %s but no matching media found", bundled[0]);
6321 if (transceivers_media_num_cmp (webrtc,
6322 get_previous_description (webrtc, sd->source, sd->sdp->type),
6324 g_set_error_literal (&error, GST_WEBRTC_ERROR,
6325 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6326 "m=lines removed from the SDP. Processing a completely new connection "
6327 "is not currently supported.");
6331 if ((sd->sdp->type == GST_WEBRTC_SDP_TYPE_PRANSWER ||
6332 sd->sdp->type == GST_WEBRTC_SDP_TYPE_ANSWER) &&
6333 transceivers_media_num_cmp (webrtc,
6334 get_last_generated_description (webrtc, sd->source, sd->sdp->type),
6336 g_set_error_literal (&error, GST_WEBRTC_ERROR,
6337 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6338 "Answer doesn't have the same number of m-lines as the offer.");
6342 if (!check_locked_mlines (webrtc, sd->sdp, &error))
6345 switch (sd->sdp->type) {
6346 case GST_WEBRTC_SDP_TYPE_OFFER:{
6347 if (sd->source == SDP_LOCAL) {
6348 if (webrtc->pending_local_description)
6349 gst_webrtc_session_description_free
6350 (webrtc->pending_local_description);
6351 webrtc->pending_local_description =
6352 gst_webrtc_session_description_copy (sd->sdp);
6353 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
6355 if (webrtc->pending_remote_description)
6356 gst_webrtc_session_description_free
6357 (webrtc->pending_remote_description);
6358 webrtc->pending_remote_description =
6359 gst_webrtc_session_description_copy (sd->sdp);
6360 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
6364 case GST_WEBRTC_SDP_TYPE_ANSWER:{
6365 if (sd->source == SDP_LOCAL) {
6366 if (webrtc->current_local_description)
6367 gst_webrtc_session_description_free
6368 (webrtc->current_local_description);
6369 webrtc->current_local_description =
6370 gst_webrtc_session_description_copy (sd->sdp);
6372 if (webrtc->current_remote_description)
6373 gst_webrtc_session_description_free
6374 (webrtc->current_remote_description);
6375 webrtc->current_remote_description = webrtc->pending_remote_description;
6376 webrtc->pending_remote_description = NULL;
6378 if (webrtc->current_remote_description)
6379 gst_webrtc_session_description_free
6380 (webrtc->current_remote_description);
6381 webrtc->current_remote_description =
6382 gst_webrtc_session_description_copy (sd->sdp);
6384 if (webrtc->current_local_description)
6385 gst_webrtc_session_description_free
6386 (webrtc->current_local_description);
6387 webrtc->current_local_description = webrtc->pending_local_description;
6388 webrtc->pending_local_description = NULL;
6391 if (webrtc->pending_local_description)
6392 gst_webrtc_session_description_free (webrtc->pending_local_description);
6393 webrtc->pending_local_description = NULL;
6395 if (webrtc->pending_remote_description)
6396 gst_webrtc_session_description_free
6397 (webrtc->pending_remote_description);
6398 webrtc->pending_remote_description = NULL;
6400 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
6403 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
6404 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
6405 if (sd->source == SDP_LOCAL) {
6406 if (webrtc->pending_local_description)
6407 gst_webrtc_session_description_free
6408 (webrtc->pending_local_description);
6409 webrtc->pending_local_description = NULL;
6411 if (webrtc->pending_remote_description)
6412 gst_webrtc_session_description_free
6413 (webrtc->pending_remote_description);
6414 webrtc->pending_remote_description = NULL;
6417 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
6420 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
6421 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
6422 if (sd->source == SDP_LOCAL) {
6423 if (webrtc->pending_local_description)
6424 gst_webrtc_session_description_free
6425 (webrtc->pending_local_description);
6426 webrtc->pending_local_description =
6427 gst_webrtc_session_description_copy (sd->sdp);
6429 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
6431 if (webrtc->pending_remote_description)
6432 gst_webrtc_session_description_free
6433 (webrtc->pending_remote_description);
6434 webrtc->pending_remote_description =
6435 gst_webrtc_session_description_copy (sd->sdp);
6437 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
6443 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
6445 * If the mid value of an RTCRtpTransceiver was set to a non-null value
6446 * by the RTCSessionDescription that is being rolled back, set the mid
6447 * value of that transceiver to null, as described by [JSEP]
6448 * (section 4.1.7.2.).
6449 * If an RTCRtpTransceiver was created by applying the
6450 * RTCSessionDescription that is being rolled back, and a track has not
6451 * been attached to it via addTrack, remove that transceiver from
6452 * connection's set of transceivers, as described by [JSEP]
6453 * (section 4.1.7.2.).
6454 * Restore the value of connection's [[ sctpTransport]] internal slot
6455 * to its value at the last stable signaling state.
6459 if (webrtc->signaling_state != new_signaling_state) {
6460 webrtc->signaling_state = new_signaling_state;
6461 signalling_state_changed = TRUE;
6465 gboolean ice_controller = FALSE;
6467 /* get the current value so we don't change ice controller from TRUE to
6468 * FALSE on renegotiation or once set to TRUE for the initial local offer */
6469 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
6471 /* we control ice negotiation if we send the initial offer */
6473 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
6474 && webrtc->current_remote_description == NULL;
6475 /* or, if the remote is an ice-lite peer */
6476 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
6477 && webrtc->current_remote_description
6478 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
6481 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
6482 ice_controller ? "true" : "false");
6483 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
6486 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6489 /* media modifications */
6490 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
6493 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
6494 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
6495 GstWebRTCRTPTransceiverDirection new_dir;
6497 const GstSDPMedia *media;
6499 if (!pad->received_caps) {
6500 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
6506 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
6511 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
6512 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
6517 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
6518 /* skip rejected media */
6519 if (gst_sdp_media_get_port (media) == 0) {
6520 /* FIXME: arrange for an appropriate flow return */
6521 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
6522 "a more correct flow return.");
6527 new_dir = pad->trans->direction;
6528 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
6529 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
6530 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
6531 "data at the moment. Not connecting input stream yet", pad->trans);
6536 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
6537 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
6538 pad->trans, pad->received_caps);
6539 _connect_input_stream (webrtc, pad);
6540 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
6544 gst_object_unref (old->data);
6545 webrtc->priv->pending_sink_transceivers =
6546 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
6550 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
6551 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
6553 TransportStream *item;
6554 guint rtp_session_id = bundled ? bundle_idx : i;
6557 _get_or_create_transport_stream (webrtc, rtp_session_id,
6558 _message_media_is_datachannel (sd->sdp->sdp, rtp_session_id));
6560 if (sd->source == SDP_REMOTE) {
6563 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
6564 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
6566 if (g_strcmp0 (attr->key, "ssrc") == 0) {
6567 GStrv split = g_strsplit (attr->value, " ", 0);
6570 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
6571 && g_str_has_prefix (split[1], "cname:")) {
6572 if (!find_mid_ssrc_for_ssrc (webrtc,
6573 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY,
6574 rtp_session_id, ssrc))
6575 transport_stream_add_ssrc_map_item (item,
6576 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, ssrc, i);
6583 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
6584 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6586 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
6587 item->stream, ufrag, pwd);
6590 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
6591 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6593 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
6594 item->stream, ufrag, pwd);
6600 if (sd->source == SDP_LOCAL) {
6601 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
6602 IceStreamItem *item =
6603 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
6605 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
6609 /* Add any pending trickle ICE candidates if we have both offer and answer */
6610 if (webrtc->current_local_description && webrtc->current_remote_description) {
6613 GstWebRTCSessionDescription *remote_sdp =
6614 webrtc->current_remote_description;
6616 /* Add any remote ICE candidates from the remote description to
6617 * support non-trickle peers first */
6618 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
6619 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
6620 _add_ice_candidates_from_sdp (webrtc, i, media);
6624 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
6625 IceCandidateItem *item =
6626 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
6627 IceCandidateItem, i);
6629 _add_ice_candidate (webrtc, item, TRUE);
6631 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
6632 ICE_UNLOCK (webrtc);
6636 * If connection's signaling state changed above, fire an event named
6637 * signalingstatechange at connection.
6639 if (signalling_state_changed) {
6640 const gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6641 webrtc->signaling_state);
6642 const gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6643 new_signaling_state);
6644 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
6647 g_object_notify (G_OBJECT (webrtc), "signaling-state");
6651 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6652 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
6654 /* If connection's signaling state is now stable, update the
6655 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
6656 * was true both before and after this update, queue a task to check
6657 * connection's [[needNegotiation]] slot and, if still true, fire a
6658 * simple event named negotiationneeded at connection.*/
6659 _update_need_negotiation (webrtc);
6660 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
6661 _check_need_negotiation_task (webrtc, NULL);
6666 g_strfreev (bundled);
6669 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6670 "error", G_TYPE_ERROR, error, NULL);
6671 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
6672 g_clear_error (&error);
6680 _free_set_description_data (struct set_description *sd)
6683 gst_webrtc_session_description_free (sd->sdp);
6688 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
6689 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
6691 struct set_description *sd;
6693 if (remote_sdp == NULL)
6695 if (remote_sdp->sdp == NULL)
6698 sd = g_new0 (struct set_description, 1);
6699 sd->source = SDP_REMOTE;
6700 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
6702 if (!gst_webrtc_bin_enqueue_task (webrtc,
6703 (GstWebRTCBinFunc) _set_description_task, sd,
6704 (GDestroyNotify) _free_set_description_data, promise)) {
6706 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6707 "Could not set remote description. webrtcbin is closed.");
6708 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6709 "error", G_TYPE_ERROR, error, NULL);
6711 gst_promise_reply (promise, s);
6713 g_clear_error (&error);
6720 gst_promise_reply (promise, NULL);
6721 g_return_if_reached ();
6726 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
6727 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
6729 struct set_description *sd;
6731 if (local_sdp == NULL)
6733 if (local_sdp->sdp == NULL)
6736 sd = g_new0 (struct set_description, 1);
6737 sd->source = SDP_LOCAL;
6738 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
6740 if (!gst_webrtc_bin_enqueue_task (webrtc,
6741 (GstWebRTCBinFunc) _set_description_task, sd,
6742 (GDestroyNotify) _free_set_description_data, promise)) {
6744 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6745 "Could not set local description. webrtcbin is closed");
6746 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6747 "error", G_TYPE_ERROR, error, NULL);
6749 gst_promise_reply (promise, s);
6751 g_clear_error (&error);
6758 gst_promise_reply (promise, NULL);
6759 g_return_if_reached ();
6763 static GstStructure *
6764 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
6766 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
6767 IceCandidateItem new;
6768 new.mlineindex = item->mlineindex;
6769 new.candidate = g_steal_pointer (&item->candidate);
6772 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
6773 ICE_UNLOCK (webrtc);
6775 _add_ice_candidate (webrtc, item, FALSE);
6782 _free_ice_candidate_item (IceCandidateItem * item)
6784 _clear_ice_candidate_item (item);
6789 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
6792 IceCandidateItem *item;
6794 item = g_new0 (IceCandidateItem, 1);
6795 item->mlineindex = mline;
6796 if (attr && attr[0] != 0) {
6797 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
6798 item->candidate = g_strdup (attr);
6799 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
6800 item->candidate = g_strdup_printf ("a=%s", attr);
6802 gst_webrtc_bin_enqueue_task (webrtc,
6803 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
6804 (GDestroyNotify) _free_ice_candidate_item, NULL);
6807 static GstStructure *
6808 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
6814 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
6815 ICE_UNLOCK (webrtc);
6816 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
6817 return NULL; /* Nothing to process */
6819 /* Take the array so we can process it all and free it later
6820 * without holding the lock
6821 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
6823 items = webrtc->priv->pending_local_ice_candidates;
6824 /* Replace with a new array */
6825 webrtc->priv->pending_local_ice_candidates =
6826 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
6827 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
6828 (GDestroyNotify) _clear_ice_candidate_item);
6829 ICE_UNLOCK (webrtc);
6831 for (i = 0; i < items->len; i++) {
6832 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
6833 const gchar *cand = item->candidate;
6835 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
6836 /* stripping away "a=" */
6840 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
6841 item->mlineindex, cand);
6843 /* First, merge this ice candidate into the appropriate mline
6844 * in the local-description SDP.
6845 * Second, emit the on-ice-candidate signal for the app.
6847 * FIXME: This ICE candidate should be stored somewhere with
6848 * the associated mid and also merged back into any subsequent
6849 * local descriptions on renegotiation */
6850 if (webrtc->current_local_description)
6851 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
6852 item->mlineindex, cand);
6853 if (webrtc->pending_local_description)
6854 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
6855 item->mlineindex, cand);
6858 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
6859 0, item->mlineindex, cand);
6863 g_array_free (items, TRUE);
6869 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
6870 gchar * candidate, GstWebRTCBin * webrtc)
6872 IceCandidateItem item;
6873 gboolean queue_task = FALSE;
6875 item.mlineindex = session_id;
6876 item.candidate = g_strdup (candidate);
6879 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
6881 /* Let the first pending candidate queue a task each time, which will
6882 * handle any that arrive between now and when the task runs */
6883 if (webrtc->priv->pending_local_ice_candidates->len == 1)
6885 ICE_UNLOCK (webrtc);
6888 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
6889 gst_webrtc_bin_enqueue_task (webrtc,
6890 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
6897 GstPromise *promise;
6901 _free_get_stats (struct get_stats *stats)
6904 gst_object_unref (stats->pad);
6906 gst_promise_unref (stats->promise);
6910 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
6911 static GstStructure *
6912 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
6914 /* Our selector is the pad,
6915 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
6918 return gst_webrtc_bin_create_stats (webrtc, stats->pad);
6922 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
6923 GstPromise * promise)
6925 struct get_stats *stats;
6927 g_return_if_fail (promise != NULL);
6928 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
6930 stats = g_new0 (struct get_stats, 1);
6931 stats->promise = gst_promise_ref (promise);
6932 /* FIXME: check that pad exists in element */
6934 stats->pad = gst_object_ref (pad);
6936 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
6937 stats, (GDestroyNotify) _free_get_stats, promise)) {
6939 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6940 "Could not retrieve statistics. webrtcbin is closed.");
6941 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6942 "error", G_TYPE_ERROR, error, NULL);
6944 gst_promise_reply (promise, s);
6946 g_clear_error (&error);
6950 static GstWebRTCRTPTransceiver *
6951 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
6952 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
6954 WebRTCTransceiver *trans;
6956 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
6962 _create_webrtc_transceiver (webrtc, direction, -1,
6963 webrtc_kind_from_caps (caps), caps);
6964 GST_LOG_OBJECT (webrtc,
6965 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
6969 return gst_object_ref (trans);
6973 _deref_and_unref (GstObject ** object)
6975 gst_clear_object (object);
6979 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
6981 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
6986 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
6988 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6989 GstWebRTCRTPTransceiver *trans =
6990 g_ptr_array_index (webrtc->priv->transceivers, i);
6991 gst_object_ref (trans);
6992 g_array_append_val (arr, trans);
6999 static GstWebRTCRTPTransceiver *
7000 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
7002 GstWebRTCRTPTransceiver *trans = NULL;
7006 if (idx >= webrtc->priv->transceivers->len) {
7007 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
7011 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
7012 gst_object_ref (trans);
7020 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
7024 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
7025 g_return_val_if_fail (uri != NULL, FALSE);
7027 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
7030 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
7037 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
7039 GstPad *gpad = GST_PAD_CAST (user_data);
7041 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
7042 gst_pad_store_sticky_event (gpad, *event);
7047 static WebRTCDataChannel *
7048 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
7049 GstStructure * init_params)
7052 gint max_packet_lifetime;
7053 gint max_retransmits;
7054 const gchar *protocol;
7055 gboolean negotiated;
7057 GstWebRTCPriorityType priority;
7058 WebRTCDataChannel *ret;
7059 gint max_channels = 65534;
7061 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
7062 g_return_val_if_fail (label != NULL, NULL);
7063 g_return_val_if_fail (strlen (label) <= 65535, NULL);
7064 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
7067 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
7070 || !gst_structure_get_int (init_params, "max-packet-lifetime",
7071 &max_packet_lifetime))
7072 max_packet_lifetime = -1;
7074 || !gst_structure_get_int (init_params, "max-retransmits",
7076 max_retransmits = -1;
7077 /* both retransmits and lifetime cannot be set */
7078 g_return_val_if_fail ((max_packet_lifetime == -1)
7079 || (max_retransmits == -1), NULL);
7082 || !(protocol = gst_structure_get_string (init_params, "protocol")))
7084 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
7087 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
7089 if (!negotiated || !init_params
7090 || !gst_structure_get_int (init_params, "id", &id))
7093 g_return_val_if_fail (id != -1, NULL);
7094 g_return_val_if_fail (id < 65535, NULL);
7097 || !gst_structure_get_enum (init_params, "priority",
7098 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
7099 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
7101 /* FIXME: clamp max-retransmits and max-packet-lifetime */
7103 if (webrtc->priv->sctp_transport) {
7104 /* Let transport be the connection's [[SctpTransport]] slot.
7106 * If the [[DataChannelId]] slot is not null, transport is in
7107 * connected state and [[DataChannelId]] is greater or equal to the
7108 * transport's [[MaxChannels]] slot, throw an OperationError.
7110 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
7113 g_return_val_if_fail (id <= max_channels, NULL);
7116 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
7117 !_have_sctp_elements (webrtc))
7122 /* check if the id has been used already */
7124 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
7126 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
7127 ("Attempting to add a data channel with a duplicate ID: %i", id),
7133 } else if (webrtc->current_local_description
7134 && webrtc->current_remote_description && webrtc->priv->sctp_transport
7135 && webrtc->priv->sctp_transport->transport) {
7136 /* else we can only generate an id if we're configured already. The other
7137 * case for generating an id is on sdp setting */
7138 id = _generate_data_channel_id (webrtc);
7140 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
7141 ("%s", "Failed to generate an identifier for a data channel"), NULL);
7148 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
7149 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
7150 "max-retransmits", max_retransmits, "protocol", protocol,
7151 "negotiated", negotiated, "id", id, "priority", priority, NULL);
7159 g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL], 0,
7162 gst_bin_add (GST_BIN (webrtc), ret->src_bin);
7163 gst_bin_add (GST_BIN (webrtc), ret->sink_bin);
7165 gst_element_sync_state_with_parent (ret->src_bin);
7166 gst_element_sync_state_with_parent (ret->sink_bin);
7168 ret = gst_object_ref (ret);
7169 webrtc_data_channel_set_webrtcbin (ret, webrtc);
7170 g_ptr_array_add (webrtc->priv->data_channels, ret);
7173 gst_webrtc_bin_update_sctp_priority (webrtc);
7174 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
7175 if (webrtc->priv->sctp_transport &&
7176 webrtc->priv->sctp_transport->association_established
7177 && !ret->parent.negotiated) {
7178 webrtc_data_channel_start_negotiation (ret);
7180 _update_need_negotiation (webrtc);
7187 /* === rtpbin signal implementations === */
7190 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
7191 GstWebRTCBin * webrtc)
7193 gchar *new_pad_name = NULL;
7195 new_pad_name = gst_pad_get_name (new_pad);
7196 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
7197 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
7198 guint32 session_id = 0, ssrc = 0, pt = 0;
7199 SsrcMapItem *mid_entry;
7200 GstWebRTCRTPTransceiver *rtp_trans = NULL;
7201 WebRTCTransceiver *trans;
7202 TransportStream *stream;
7203 GstWebRTCBinPad *pad;
7206 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
7208 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
7212 media_idx = session_id;
7215 stream = _find_transport_for_session (webrtc, session_id);
7217 g_warn_if_reached ();
7220 find_mid_ssrc_for_ssrc (webrtc,
7221 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc);
7224 if (mid_entry->mid) {
7225 /* Can't use the mid_entry if the mid doesn't exist */
7226 rtp_trans = _find_transceiver_for_mid (webrtc, mid_entry->mid);
7228 g_assert_cmpint (rtp_trans->mline, ==, mid_entry->media_idx);
7232 if (mid_entry->media_idx != -1)
7233 media_idx = mid_entry->media_idx;
7235 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
7236 /* TODO: connect up to fakesink and reconnect later when this information
7237 * is known from RTCP SDES or RTP Header extension
7242 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
7244 g_warn_if_reached ();
7245 trans = WEBRTC_TRANSCEIVER (rtp_trans);
7246 g_assert (trans->stream == stream);
7248 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
7249 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
7250 " for rtpbin pad name %s", pad, new_pad_name);
7251 if (!_remove_pending_pad (webrtc, pad)) {
7252 /* assumption here is that rtpbin doesn't duplicate pads and that if
7253 * there is no pending pad, this is a duplicate stream for e.g. simulcast
7255 gst_clear_object (&pad);
7257 _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT,
7259 GST_TRACE_OBJECT (webrtc,
7260 "duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %"
7261 GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name);
7262 gst_object_ref_sink (pad);
7266 g_warn_if_reached ();
7267 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
7269 if (webrtc->priv->running)
7270 gst_pad_set_active (GST_PAD (pad), TRUE);
7274 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
7275 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
7277 gst_object_unref (pad);
7279 g_free (new_pad_name);
7282 /* only used for the receiving streams */
7284 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
7285 GstWebRTCBin * webrtc)
7287 TransportStream *stream;
7290 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
7294 stream = _find_transport_for_session (webrtc, session_id);
7296 goto unknown_session;
7298 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
7301 GST_DEBUG_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
7302 "session %d", ret, pt, session_id);
7310 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
7316 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
7317 GstWebRTCBin * webrtc)
7319 TransportStream *stream;
7320 GstElement *ret, *rtx;
7323 GstElement *aux_sender = NULL;
7325 stream = _find_transport_for_session (webrtc, session_id);
7327 /* a rtp session without a stream is a webrtcbin bug */
7328 g_warn_if_reached ();
7332 if (stream->rtxsend) {
7333 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
7334 g_warn_if_reached ();
7338 GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
7339 "stream %" GST_PTR_FORMAT, session_id, stream);
7341 ret = gst_bin_new (NULL);
7342 rtx = gst_element_factory_make ("rtprtxsend", NULL);
7343 /* XXX: allow control from outside? */
7344 g_object_set (rtx, "max-size-packets", 500, NULL);
7346 if (!gst_bin_add (GST_BIN (ret), rtx))
7347 g_warn_if_reached ();
7348 ensure_rtx_hdr_ext (stream);
7350 stream->rtxsend = gst_object_ref (rtx);
7351 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7353 name = g_strdup_printf ("src_%u", session_id);
7354 pad = gst_element_get_static_pad (rtx, "src");
7357 g_signal_emit (webrtc, gst_webrtc_bin_signals[REQUEST_AUX_SENDER], 0,
7358 stream->transport, &aux_sender);
7360 GstPadLinkReturn link_res;
7361 GstPad *sinkpad = gst_element_get_static_pad (aux_sender, "sink");
7362 GstPad *srcpad = gst_element_get_static_pad (aux_sender, "src");
7364 gst_object_ref_sink (aux_sender);
7366 if (!sinkpad || !srcpad) {
7367 GST_ERROR_OBJECT (webrtc,
7368 "Invalid pads for the aux sender %" GST_PTR_FORMAT
7369 ". Skipping it.", aux_sender);
7373 if (!gst_bin_add (GST_BIN (ret), aux_sender)) {
7374 GST_ERROR_OBJECT (webrtc,
7375 "Could not add aux sender %" GST_PTR_FORMAT, aux_sender);
7379 link_res = gst_pad_link (pad, sinkpad);
7380 if (link_res != GST_PAD_LINK_OK) {
7381 GST_ERROR_OBJECT (webrtc,
7382 "Could not link aux sender %" GST_PTR_FORMAT " %s", aux_sender,
7383 gst_pad_link_get_name (link_res));
7387 gst_clear_object (&pad);
7388 pad = gst_object_ref (srcpad);
7391 if (pad != srcpad) {
7392 /* Failed using the provided aux sender */
7393 if (gst_object_has_as_parent (GST_OBJECT (aux_sender), GST_OBJECT (ret))) {
7394 gst_bin_remove (GST_BIN (ret), aux_sender);
7397 gst_clear_object (&aux_sender);
7398 gst_clear_object (&srcpad);
7399 gst_clear_object (&sinkpad);
7402 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7403 g_warn_if_reached ();
7404 gst_clear_object (&pad);
7405 g_clear_pointer (&name, g_free);
7407 name = g_strdup_printf ("sink_%u", session_id);
7408 pad = gst_element_get_static_pad (rtx, "sink");
7409 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7410 g_warn_if_reached ();
7411 gst_clear_object (&pad);
7412 g_clear_pointer (&name, g_free);
7418 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
7419 GstWebRTCBin * webrtc)
7421 TransportStream *stream;
7422 GstPad *pad, *ghost;
7426 stream = _find_transport_for_session (webrtc, session_id);
7428 /* no transport stream before the session has been created is a webrtcbin
7429 * programming error! */
7430 g_warn_if_reached ();
7434 if (stream->rtxreceive) {
7435 GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
7436 g_warn_if_reached ();
7440 if (stream->reddec) {
7441 GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
7442 g_warn_if_reached ();
7446 GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
7447 "stream %" GST_PTR_FORMAT, session_id, stream);
7449 ret = gst_bin_new (NULL);
7451 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
7452 gst_object_ref (stream->rtxreceive);
7453 if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
7454 g_warn_if_reached ();
7456 ensure_rtx_hdr_ext (stream);
7458 stream->reddec = gst_element_factory_make ("rtpreddec", NULL);
7459 gst_object_ref (stream->reddec);
7460 if (!gst_bin_add (GST_BIN (ret), stream->reddec))
7461 g_warn_if_reached ();
7463 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7465 if (!gst_element_link (stream->rtxreceive, stream->reddec))
7466 g_warn_if_reached ();
7468 name = g_strdup_printf ("sink_%u", session_id);
7469 pad = gst_element_get_static_pad (stream->rtxreceive, "sink");
7470 ghost = gst_ghost_pad_new (name, pad);
7471 g_clear_pointer (&name, g_free);
7472 gst_clear_object (&pad);
7473 if (!gst_element_add_pad (ret, ghost))
7474 g_warn_if_reached ();
7476 name = g_strdup_printf ("src_%u", session_id);
7477 pad = gst_element_get_static_pad (stream->reddec, "src");
7478 ghost = gst_ghost_pad_new (name, pad);
7479 g_clear_pointer (&name, g_free);
7480 gst_clear_object (&pad);
7481 if (!gst_element_add_pad (ret, ghost))
7482 g_warn_if_reached ();
7488 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
7489 guint ssrc, guint pt, GstWebRTCBin * webrtc)
7491 TransportStream *stream;
7492 GstElement *ret = NULL;
7493 GObject *internal_storage;
7495 stream = _find_transport_for_session (webrtc, session_id);
7497 /* a rtp session without a stream is a webrtcbin bug */
7498 g_warn_if_reached ();
7502 /* TODO: for now, we only support ulpfec, but once we support
7503 * more algorithms, if the remote may use more than one algorithm,
7504 * we will want to do the following:
7506 * + Return a bin here, with the relevant FEC decoders plugged in
7507 * and their payload type set to 0
7509 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
7510 "stream %" GST_PTR_FORMAT, pt, session_id, stream);
7512 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
7514 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
7517 g_object_set (ret, "storage", internal_storage, NULL);
7518 g_clear_object (&internal_storage);
7520 g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
7521 GINT_TO_POINTER (pt));
7524 stream->fecdecs = g_list_prepend (stream->fecdecs, gst_object_ref (ret));
7525 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7532 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7533 GstWebRTCBin * webrtc)
7535 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
7538 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7543 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7544 GstWebRTCBin * webrtc)
7546 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
7549 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7554 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7555 GstWebRTCBin * webrtc)
7557 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
7561 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7566 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7567 GstWebRTCBin * webrtc)
7569 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
7575 find_or_add_ssrc_map_item (webrtc,
7576 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc, -1);
7581 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7582 GstWebRTCBin * webrtc)
7584 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
7588 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
7589 GstWebRTCBin * webrtc)
7591 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
7595 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
7596 GstWebRTCBin * webrtc)
7600 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
7602 g_signal_emit_by_name (rtpbin, "get-internal-session", session_id, &session);
7606 g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7610 g_object_get (source, "sdes", &sdes, NULL);
7612 /* TODO: when the sdes contains the mid, use that to correlate streams
7614 GST_DEBUG_OBJECT (webrtc, "session %u ssrc %u sdes %" GST_PTR_FORMAT,
7615 session_id, ssrc, sdes);
7617 gst_clear_structure (&sdes);
7618 gst_clear_object (&source);
7620 g_clear_object (&session);
7625 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
7626 GstWebRTCBin * webrtc)
7628 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
7632 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7633 GstWebRTCBin * webrtc)
7635 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
7638 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7643 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7644 GstWebRTCBin * webrtc)
7648 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
7652 mid = find_mid_ssrc_for_ssrc (webrtc,
7653 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, session_id, ssrc);
7655 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
7656 transport_stream_add_ssrc_map_item (stream,
7657 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, ssrc, -1);
7658 } else if (mid->mid) {
7659 /* XXX: when peers support the sdes rtcp item, use this to send the mid rtcp
7660 * sdes item. Requires being able to set the sdes on the rtpsource. */
7664 g_signal_emit_by_name (rtpbin, "get-internal-session", session_id,
7669 g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7672 const char *sdes_field_name;
7674 g_object_get (source, "sdes", &sdes, NULL);
7675 GST_WARNING_OBJECT (webrtc, "session %u ssrc %u retrieve sdes %"
7676 GST_PTR_FORMAT, session_id, ssrc, sdes);
7677 sdes_field_name = gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_MID);
7678 g_assert (sdes_field_name);
7679 gst_structure_set (sdes, sdes_field_name, G_TYPE_STRING, mid->mid,
7683 gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_RTP_STREAM_ID);
7684 g_assert (sdes_field_name);
7685 gst_structure_set (sdes, sdes_field_name, mid->rid, NULL);
7686 // TODO: repaired-rtp-stream-id
7688 // TODO: writable sdes?
7689 g_object_set (source, "sdes", sdes, NULL);
7690 GST_INFO_OBJECT (webrtc,
7691 "session %u ssrc %u set sdes %" GST_PTR_FORMAT, session_id, ssrc,
7694 gst_clear_structure (&sdes);
7695 gst_clear_object (&source);
7697 g_clear_object (&session);
7705 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7706 GstWebRTCBin * webrtc)
7708 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
7714 GstWebRTCBin *webrtc;
7715 GstElement *jitterbuffer;
7716 TransportStream *stream;
7721 jitter_buffer_set_retransmission (SsrcMapItem * item,
7722 const struct new_jb_args *data)
7724 GstWebRTCRTPTransceiver *trans;
7727 if (item->media_idx == -1)
7730 trans = _find_transceiver_for_mline (data->webrtc, item->media_idx);
7732 g_warn_if_reached ();
7736 do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
7737 /* We don't set do-retransmission on rtpbin as we want per-session control */
7738 GST_LOG_OBJECT (data->webrtc, "setting do-nack=%s for transceiver %"
7739 GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
7740 " rtp session %u ssrc %u", do_nack ? "true" : "false", trans,
7741 data->stream, data->stream->session_id, data->ssrc);
7742 g_object_set (data->jitterbuffer, "do-retransmission", do_nack, NULL);
7744 g_weak_ref_set (&item->rtpjitterbuffer, data->jitterbuffer);
7750 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
7751 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
7753 TransportStream *stream;
7754 struct new_jb_args d = { 0, };
7757 GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
7758 "session %u ssrc %u", jitterbuffer, session_id, ssrc);
7760 if (!(stream = _find_transport_for_session (webrtc, session_id))) {
7761 g_warn_if_reached ();
7766 d.jitterbuffer = jitterbuffer;
7769 transport_stream_filter_ssrc_map_item (stream, &d,
7770 (FindSsrcMapFunc) jitter_buffer_set_retransmission);
7777 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
7778 guint session_id, GstWebRTCBin * webrtc)
7780 guint64 latency = webrtc->priv->jb_latency;
7782 /* Add an extra 50 ms for safey */
7783 latency += RTPSTORAGE_EXTRA_TIME;
7784 latency *= GST_MSECOND;
7786 g_object_set (storage, "size-time", latency, NULL);
7790 _create_rtpbin (GstWebRTCBin * webrtc)
7794 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
7797 /* mandated by WebRTC */
7798 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
7800 g_object_set (rtpbin, "do-lost", TRUE, NULL);
7802 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
7804 g_signal_connect (rtpbin, "request-pt-map",
7805 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
7806 g_signal_connect (rtpbin, "request-aux-sender",
7807 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
7808 g_signal_connect (rtpbin, "request-aux-receiver",
7809 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
7810 g_signal_connect (rtpbin, "new-storage",
7811 G_CALLBACK (on_rtpbin_new_storage), webrtc);
7812 g_signal_connect (rtpbin, "request-fec-decoder-full",
7813 G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
7814 g_signal_connect (rtpbin, "on-bye-ssrc",
7815 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
7816 g_signal_connect (rtpbin, "on-bye-timeout",
7817 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
7818 g_signal_connect (rtpbin, "on-new-ssrc",
7819 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
7820 g_signal_connect (rtpbin, "on-new-sender-ssrc",
7821 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
7822 g_signal_connect (rtpbin, "on-sender-ssrc-active",
7823 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
7824 g_signal_connect (rtpbin, "on-sender-timeout",
7825 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
7826 g_signal_connect (rtpbin, "on-ssrc-active",
7827 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
7828 g_signal_connect (rtpbin, "on-ssrc-collision",
7829 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
7830 g_signal_connect (rtpbin, "on-ssrc-sdes",
7831 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
7832 g_signal_connect (rtpbin, "on-ssrc-validated",
7833 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
7834 g_signal_connect (rtpbin, "on-timeout",
7835 G_CALLBACK (on_rtpbin_timeout), webrtc);
7836 g_signal_connect (rtpbin, "new-jitterbuffer",
7837 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
7842 static GstStateChangeReturn
7843 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
7845 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7846 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
7848 GST_DEBUG ("changing state: %s => %s",
7849 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
7850 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
7852 switch (transition) {
7853 case GST_STATE_CHANGE_NULL_TO_READY:{
7854 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
7855 return GST_STATE_CHANGE_FAILURE;
7856 _start_thread (webrtc);
7858 _update_need_negotiation (webrtc);
7862 case GST_STATE_CHANGE_READY_TO_PAUSED:
7863 webrtc->priv->running = TRUE;
7869 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
7870 if (ret == GST_STATE_CHANGE_FAILURE)
7873 switch (transition) {
7874 case GST_STATE_CHANGE_READY_TO_PAUSED:
7875 /* Mangle the return value to NO_PREROLL as that's what really is
7876 * occurring here however cannot be propagated correctly due to nicesrc
7877 * requiring that it be in PLAYING already in order to send/receive
7879 ret = GST_STATE_CHANGE_NO_PREROLL;
7881 case GST_STATE_CHANGE_PAUSED_TO_READY:
7882 webrtc->priv->running = FALSE;
7884 case GST_STATE_CHANGE_READY_TO_NULL:
7885 _stop_thread (webrtc);
7894 static GstPadProbeReturn
7895 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
7897 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
7899 return GST_PAD_PROBE_OK;
7903 peek_sink_buffer (GstWebRTCBin * webrtc, guint rtp_session_id,
7904 guint media_idx, WebRTCTransceiver * trans, GstBuffer * buffer)
7906 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
7910 if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp))
7912 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
7913 gst_rtp_buffer_unmap (&rtp);
7916 GST_WARNING_OBJECT (webrtc,
7917 "incoming buffer does not contain a valid ssrc");
7923 find_or_add_ssrc_map_item (webrtc,
7924 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session_id, ssrc,
7926 if (item->media_idx == -1) {
7929 GST_DEBUG_OBJECT (webrtc, "updating media idx of ssrc item %p to %u", item,
7931 item->media_idx = media_idx;
7933 /* ensure that the rtx mapping contains a valid ssrc to use for rtx when
7934 * used even when there are no ssrc's in the input/codec preferences caps */
7935 str = g_strdup_printf ("%u", ssrc);
7936 if (!gst_structure_has_field_typed (trans->local_rtx_ssrc_map, str,
7938 /* TODO: ssrc-collision? */
7939 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
7940 g_random_int (), NULL);
7941 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
7948 static GstPadProbeReturn
7949 sink_pad_buffer_peek (GstPad * pad, GstPadProbeInfo * info,
7950 GstWebRTCBin * webrtc)
7952 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7953 WebRTCTransceiver *trans;
7954 guint rtp_session_id, media_idx;
7956 if (!webrtc_pad->trans)
7957 return GST_PAD_PROBE_OK;
7959 trans = (WebRTCTransceiver *) webrtc_pad->trans;
7961 return GST_PAD_PROBE_OK;
7963 rtp_session_id = trans->stream->session_id;
7964 media_idx = webrtc_pad->trans->mline;
7966 if (media_idx != G_MAXUINT)
7967 return GST_PAD_PROBE_OK;
7969 if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
7970 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
7971 peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
7972 } else if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
7973 GstBufferList *list = GST_PAD_PROBE_INFO_BUFFER_LIST (info);
7976 n = gst_buffer_list_length (list);
7977 for (i = 0; i < n; i++) {
7978 GstBuffer *buffer = gst_buffer_list_get (list, i);
7979 peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
7982 g_assert_not_reached ();
7985 return GST_PAD_PROBE_OK;
7989 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
7990 const gchar * name, const GstCaps * caps)
7992 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7993 GstWebRTCRTPTransceiver *trans = NULL;
7994 GstWebRTCBinPad *pad = NULL;
7996 gboolean lock_mline = FALSE;
7998 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
8001 if (templ->direction != GST_PAD_SINK ||
8002 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
8003 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
8009 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
8010 /* no name given when requesting the pad, use next available int */
8011 serial = webrtc->priv->max_sink_pad_serial++;
8013 /* parse serial number from requested padname */
8014 serial = g_ascii_strtoull (&name[5], NULL, 10);
8019 GstWebRTCBinPad *pad2;
8021 trans = _find_transceiver_for_mline (webrtc, serial);
8024 /* Reject transceivers that are only for receiving ... */
8025 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
8026 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
8027 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8028 " existing m-line %d, but the transceiver's direction is %s",
8030 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction));
8034 /* Reject transceivers that already have a pad allocated */
8035 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
8037 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
8038 " but the transceiver associated with this m-line already has pad"
8039 " %s", name, serial, GST_PAD_NAME (pad2));
8040 gst_object_unref (pad2);
8045 GST_OBJECT_LOCK (trans);
8046 if (trans->codec_preferences &&
8047 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
8048 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8049 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
8050 " don't match existing codec preferences %" GST_PTR_FORMAT,
8051 name, serial, caps, trans->codec_preferences);
8052 GST_OBJECT_UNLOCK (trans);
8055 GST_OBJECT_UNLOCK (trans);
8057 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
8058 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
8060 if (trans->kind != kind) {
8061 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8062 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
8063 " don't match transceiver kind %d",
8064 name, serial, caps, trans->kind);
8072 /* Let's try to find a free transceiver that matches */
8074 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
8077 kind = webrtc_kind_from_caps (caps);
8079 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
8080 GstWebRTCRTPTransceiver *tmptrans =
8081 g_ptr_array_index (webrtc->priv->transceivers, i);
8082 GstWebRTCBinPad *pad2;
8083 gboolean has_matching_caps;
8085 /* Ignore transceivers with a non-matching kind */
8086 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
8087 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
8090 /* Ignore stopped transmitters */
8091 if (tmptrans->stopped)
8094 /* Ignore transceivers that are only for receiving ... */
8095 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
8096 || tmptrans->direction ==
8097 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
8100 /* Ignore transceivers that already have a pad allocated */
8101 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
8103 gst_object_unref (pad2);
8107 GST_OBJECT_LOCK (tmptrans);
8108 has_matching_caps = (caps && tmptrans->codec_preferences &&
8109 !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
8110 GST_OBJECT_UNLOCK (tmptrans);
8111 /* Ignore transceivers with non-matching caps */
8112 if (!has_matching_caps)
8121 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
8122 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
8123 webrtc_kind_from_caps (caps), NULL));
8124 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
8126 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
8127 " for mline %u", trans, serial);
8129 if (!_update_transceiver_kind_from_caps (trans, caps)) {
8130 GstWebRTCKind caps_kind = webrtc_kind_from_caps (caps);
8132 GST_WARNING_OBJECT (webrtc,
8133 "Trying to change kind of transceiver %" GST_PTR_FORMAT
8134 " at m-line %d from %s (%d) to %s (%d)", trans, serial,
8135 gst_webrtc_kind_to_string (trans->kind), trans->kind,
8136 gst_webrtc_kind_to_string (caps_kind), caps_kind);
8140 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial, NULL);
8142 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
8143 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
8144 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
8145 webrtc->priv->pending_sink_transceivers =
8146 g_list_append (webrtc->priv->pending_sink_transceivers,
8147 gst_object_ref (pad));
8149 gst_pad_add_probe (GST_PAD (pad),
8150 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
8151 (GstPadProbeCallback) sink_pad_buffer_peek, webrtc, NULL);
8154 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
8155 wtrans->mline_locked = TRUE;
8156 trans->mline = serial;
8161 _add_pad (webrtc, pad);
8163 return GST_PAD (pad);
8171 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
8173 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
8174 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
8176 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
8178 /* remove the transceiver from the pad so that subsequent code doesn't use
8179 * a possibly dead transceiver */
8181 if (webrtc_pad->trans)
8182 gst_object_unref (webrtc_pad->trans);
8183 webrtc_pad->trans = NULL;
8184 gst_caps_replace (&webrtc_pad->received_caps, NULL);
8187 if (webrtc_pad->block_id) {
8188 gst_pad_remove_probe (GST_PAD (pad), webrtc_pad->block_id);
8189 webrtc_pad->block_id = 0;
8192 _remove_pad (webrtc, webrtc_pad);
8195 _update_need_negotiation (webrtc);
8200 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
8205 /* Add an extra 50 ms for safety */
8206 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
8207 latency_ns *= GST_MSECOND;
8209 for (i = 0; i < webrtc->priv->transports->len; i++) {
8210 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
8211 GObject *storage = NULL;
8213 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
8216 g_object_set (storage, "size-time", latency_ns, NULL);
8218 g_object_unref (storage);
8223 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
8224 const GValue * value, GParamSpec * pspec)
8226 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8229 case PROP_STUN_SERVER:
8230 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
8231 g_value_get_string (value));
8233 case PROP_TURN_SERVER:
8234 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
8235 g_value_get_string (value));
8237 case PROP_BUNDLE_POLICY:
8238 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
8239 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
8241 webrtc->bundle_policy = g_value_get_enum (value);
8244 case PROP_ICE_TRANSPORT_POLICY:
8245 webrtc->ice_transport_policy = g_value_get_enum (value);
8246 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
8247 webrtc->ice_transport_policy ==
8248 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
8251 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
8252 webrtc->priv->jb_latency = g_value_get_uint (value);
8253 _update_rtpstorage_latency (webrtc);
8255 case PROP_ICE_AGENT:
8256 webrtc->priv->ice = g_value_get_object (value);
8258 case PROP_HTTP_PROXY:
8259 gst_webrtc_ice_set_http_proxy (webrtc->priv->ice,
8260 g_value_get_string (value));
8263 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8269 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
8270 GValue * value, GParamSpec * pspec)
8272 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8276 case PROP_CONNECTION_STATE:
8277 g_value_set_enum (value, webrtc->peer_connection_state);
8279 case PROP_SIGNALING_STATE:
8280 g_value_set_enum (value, webrtc->signaling_state);
8282 case PROP_ICE_GATHERING_STATE:
8283 g_value_set_enum (value, webrtc->ice_gathering_state);
8285 case PROP_ICE_CONNECTION_STATE:
8286 g_value_set_enum (value, webrtc->ice_connection_state);
8288 case PROP_LOCAL_DESCRIPTION:
8289 if (webrtc->pending_local_description)
8290 g_value_set_boxed (value, webrtc->pending_local_description);
8291 else if (webrtc->current_local_description)
8292 g_value_set_boxed (value, webrtc->current_local_description);
8294 g_value_set_boxed (value, NULL);
8296 case PROP_CURRENT_LOCAL_DESCRIPTION:
8297 g_value_set_boxed (value, webrtc->current_local_description);
8299 case PROP_PENDING_LOCAL_DESCRIPTION:
8300 g_value_set_boxed (value, webrtc->pending_local_description);
8302 case PROP_REMOTE_DESCRIPTION:
8303 if (webrtc->pending_remote_description)
8304 g_value_set_boxed (value, webrtc->pending_remote_description);
8305 else if (webrtc->current_remote_description)
8306 g_value_set_boxed (value, webrtc->current_remote_description);
8308 g_value_set_boxed (value, NULL);
8310 case PROP_CURRENT_REMOTE_DESCRIPTION:
8311 g_value_set_boxed (value, webrtc->current_remote_description);
8313 case PROP_PENDING_REMOTE_DESCRIPTION:
8314 g_value_set_boxed (value, webrtc->pending_remote_description);
8316 case PROP_STUN_SERVER:
8317 g_value_take_string (value,
8318 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
8320 case PROP_TURN_SERVER:
8321 g_value_take_string (value,
8322 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
8324 case PROP_BUNDLE_POLICY:
8325 g_value_set_enum (value, webrtc->bundle_policy);
8327 case PROP_ICE_TRANSPORT_POLICY:
8328 g_value_set_enum (value, webrtc->ice_transport_policy);
8330 case PROP_ICE_AGENT:
8331 g_value_set_object (value, webrtc->priv->ice);
8334 g_value_set_uint (value, webrtc->priv->jb_latency);
8336 case PROP_SCTP_TRANSPORT:
8337 g_value_set_object (value, webrtc->priv->sctp_transport);
8339 case PROP_HTTP_PROXY:
8340 g_value_take_string (value,
8341 gst_webrtc_ice_get_http_proxy (webrtc->priv->ice));
8344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8351 gst_webrtc_bin_constructed (GObject * object)
8353 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8356 if (!webrtc->priv->ice) {
8357 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
8358 webrtc->priv->ice = GST_WEBRTC_ICE (gst_webrtc_nice_new (name));
8361 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
8362 (GstWebRTCICEOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
8364 G_OBJECT_CLASS (parent_class)->constructed (object);
8368 _free_pending_pad (GstPad * pad)
8370 gst_object_unref (pad);
8374 gst_webrtc_bin_dispose (GObject * object)
8376 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8378 if (webrtc->priv->ice)
8379 gst_object_unref (webrtc->priv->ice);
8380 webrtc->priv->ice = NULL;
8382 if (webrtc->priv->ice_stream_map)
8383 g_array_free (webrtc->priv->ice_stream_map, TRUE);
8384 webrtc->priv->ice_stream_map = NULL;
8386 g_clear_object (&webrtc->priv->sctp_transport);
8388 G_OBJECT_CLASS (parent_class)->dispose (object);
8392 gst_webrtc_bin_finalize (GObject * object)
8394 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8396 if (webrtc->priv->transports)
8397 g_ptr_array_free (webrtc->priv->transports, TRUE);
8398 webrtc->priv->transports = NULL;
8400 if (webrtc->priv->transceivers)
8401 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
8402 webrtc->priv->transceivers = NULL;
8404 if (webrtc->priv->data_channels)
8405 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
8406 webrtc->priv->data_channels = NULL;
8408 if (webrtc->priv->pending_data_channels)
8409 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
8410 webrtc->priv->pending_data_channels = NULL;
8412 if (webrtc->priv->pending_remote_ice_candidates)
8413 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
8414 webrtc->priv->pending_remote_ice_candidates = NULL;
8416 if (webrtc->priv->pending_local_ice_candidates)
8417 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
8418 webrtc->priv->pending_local_ice_candidates = NULL;
8420 if (webrtc->priv->pending_pads)
8421 g_list_free_full (webrtc->priv->pending_pads,
8422 (GDestroyNotify) _free_pending_pad);
8423 webrtc->priv->pending_pads = NULL;
8425 if (webrtc->priv->pending_sink_transceivers)
8426 g_list_free_full (webrtc->priv->pending_sink_transceivers,
8427 (GDestroyNotify) gst_object_unref);
8428 webrtc->priv->pending_sink_transceivers = NULL;
8430 if (webrtc->current_local_description)
8431 gst_webrtc_session_description_free (webrtc->current_local_description);
8432 webrtc->current_local_description = NULL;
8433 if (webrtc->pending_local_description)
8434 gst_webrtc_session_description_free (webrtc->pending_local_description);
8435 webrtc->pending_local_description = NULL;
8437 if (webrtc->current_remote_description)
8438 gst_webrtc_session_description_free (webrtc->current_remote_description);
8439 webrtc->current_remote_description = NULL;
8440 if (webrtc->pending_remote_description)
8441 gst_webrtc_session_description_free (webrtc->pending_remote_description);
8442 webrtc->pending_remote_description = NULL;
8444 if (webrtc->priv->last_generated_answer)
8445 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
8446 webrtc->priv->last_generated_answer = NULL;
8447 if (webrtc->priv->last_generated_offer)
8448 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
8449 webrtc->priv->last_generated_offer = NULL;
8451 g_mutex_clear (DC_GET_LOCK (webrtc));
8452 g_mutex_clear (ICE_GET_LOCK (webrtc));
8453 g_mutex_clear (PC_GET_LOCK (webrtc));
8454 g_cond_clear (PC_GET_COND (webrtc));
8456 G_OBJECT_CLASS (parent_class)->finalize (object);
8460 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
8462 GObjectClass *gobject_class = (GObjectClass *) klass;
8463 GstElementClass *element_class = (GstElementClass *) klass;
8465 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
8466 element_class->release_pad = gst_webrtc_bin_release_pad;
8467 element_class->change_state = gst_webrtc_bin_change_state;
8469 gst_element_class_add_static_pad_template_with_gtype (element_class,
8470 &sink_template, GST_TYPE_WEBRTC_BIN_SINK_PAD);
8471 gst_element_class_add_static_pad_template_with_gtype (element_class,
8472 &src_template, GST_TYPE_WEBRTC_BIN_SRC_PAD);
8474 gst_element_class_set_metadata (element_class, "WebRTC Bin",
8475 "Filter/Network/WebRTC", "A bin for webrtc connections",
8476 "Matthew Waters <matthew@centricular.com>");
8478 gobject_class->constructed = gst_webrtc_bin_constructed;
8479 gobject_class->get_property = gst_webrtc_bin_get_property;
8480 gobject_class->set_property = gst_webrtc_bin_set_property;
8481 gobject_class->dispose = gst_webrtc_bin_dispose;
8482 gobject_class->finalize = gst_webrtc_bin_finalize;
8484 g_object_class_install_property (gobject_class,
8485 PROP_LOCAL_DESCRIPTION,
8486 g_param_spec_boxed ("local-description", "Local Description",
8487 "The local SDP description in use for this connection. "
8488 "Favours a pending description over the current description",
8489 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8490 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8492 g_object_class_install_property (gobject_class,
8493 PROP_CURRENT_LOCAL_DESCRIPTION,
8494 g_param_spec_boxed ("current-local-description",
8495 "Current Local Description",
8496 "The local description that was successfully negotiated the last time "
8497 "the connection transitioned into the stable state",
8498 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8499 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8501 g_object_class_install_property (gobject_class,
8502 PROP_PENDING_LOCAL_DESCRIPTION,
8503 g_param_spec_boxed ("pending-local-description",
8504 "Pending Local Description",
8505 "The local description that is in the process of being negotiated plus "
8506 "any local candidates that have been generated by the ICE Agent since the "
8507 "offer or answer was created",
8508 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8509 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8511 g_object_class_install_property (gobject_class,
8512 PROP_REMOTE_DESCRIPTION,
8513 g_param_spec_boxed ("remote-description", "Remote Description",
8514 "The remote SDP description to use for this connection. "
8515 "Favours a pending description over the current description",
8516 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8517 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8519 g_object_class_install_property (gobject_class,
8520 PROP_CURRENT_REMOTE_DESCRIPTION,
8521 g_param_spec_boxed ("current-remote-description",
8522 "Current Remote Description",
8523 "The last remote description that was successfully negotiated the last "
8524 "time the connection transitioned into the stable state plus any remote "
8525 "candidates that have been supplied via addIceCandidate() since the offer "
8526 "or answer was created",
8527 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8528 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8530 g_object_class_install_property (gobject_class,
8531 PROP_PENDING_REMOTE_DESCRIPTION,
8532 g_param_spec_boxed ("pending-remote-description",
8533 "Pending Remote Description",
8534 "The remote description that is in the process of being negotiated, "
8535 "complete with any remote candidates that have been supplied via "
8536 "addIceCandidate() since the offer or answer was created",
8537 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8538 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8540 g_object_class_install_property (gobject_class,
8542 g_param_spec_string ("stun-server", "STUN Server",
8543 "The STUN server of the form stun://hostname:port",
8544 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8546 g_object_class_install_property (gobject_class,
8548 g_param_spec_string ("turn-server", "TURN Server",
8549 "The TURN server of the form turn(s)://username:password@host:port. "
8550 "To use time-limited credentials, the form must be turn(s)://timestamp:"
8551 "username:password@host:port. Please note that the ':' character of "
8552 "the 'timestamp:username' and the 'password' encoded by base64 should "
8553 "be escaped to be parsed properly. "
8554 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
8555 "if you wish to use multiple TURN servers",
8556 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8558 g_object_class_install_property (gobject_class,
8559 PROP_CONNECTION_STATE,
8560 g_param_spec_enum ("connection-state", "Connection State",
8561 "The overall connection state of this element",
8562 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
8563 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
8564 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8566 g_object_class_install_property (gobject_class,
8567 PROP_SIGNALING_STATE,
8568 g_param_spec_enum ("signaling-state", "Signaling State",
8569 "The signaling state of this element",
8570 GST_TYPE_WEBRTC_SIGNALING_STATE,
8571 GST_WEBRTC_SIGNALING_STATE_STABLE,
8572 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8574 g_object_class_install_property (gobject_class,
8575 PROP_ICE_CONNECTION_STATE,
8576 g_param_spec_enum ("ice-connection-state", "ICE connection state",
8577 "The collective connection state of all ICETransport's",
8578 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
8579 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
8580 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8582 g_object_class_install_property (gobject_class,
8583 PROP_ICE_GATHERING_STATE,
8584 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
8585 "The collective gathering state of all ICETransport's",
8586 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
8587 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
8588 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8590 g_object_class_install_property (gobject_class,
8592 g_param_spec_enum ("bundle-policy", "Bundle Policy",
8593 "The policy to apply for bundling",
8594 GST_TYPE_WEBRTC_BUNDLE_POLICY,
8595 GST_WEBRTC_BUNDLE_POLICY_NONE,
8596 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8598 g_object_class_install_property (gobject_class,
8599 PROP_ICE_TRANSPORT_POLICY,
8600 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
8601 "The policy to apply for ICE transport",
8602 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
8603 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
8604 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8606 g_object_class_install_property (gobject_class,
8608 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
8609 "The WebRTC ICE agent",
8610 GST_TYPE_WEBRTC_ICE,
8611 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
8614 * GstWebRTCBin:latency:
8616 * Default duration to buffer in the jitterbuffers (in ms)
8621 g_object_class_install_property (gobject_class,
8623 g_param_spec_uint ("latency", "Latency",
8624 "Default duration to buffer in the jitterbuffers (in ms)",
8625 0, G_MAXUINT, DEFAULT_JB_LATENCY,
8626 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8629 * GstWebRTCBin:http-proxy:
8631 * A HTTP proxy for use with TURN/TCP of the form
8632 * http://[username:password@]hostname[:port]
8636 g_object_class_install_property (gobject_class,
8638 g_param_spec_string ("http-proxy", "HTTP Proxy",
8639 "A HTTP proxy for use with TURN/TCP of the form "
8640 "http://[username:password@]hostname[:port]",
8641 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8644 * GstWebRTCBin:sctp-transport:
8646 * The WebRTC SCTP Transport
8650 g_object_class_install_property (gobject_class,
8651 PROP_SCTP_TRANSPORT,
8652 g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
8653 "The WebRTC SCTP Transport",
8654 GST_TYPE_WEBRTC_SCTP_TRANSPORT,
8655 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8658 * GstWebRTCBin::create-offer:
8659 * @object: the #webrtcbin
8660 * @options: (nullable): create-offer options
8661 * @promise: a #GstPromise which will contain the offer
8663 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
8664 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
8665 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8666 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
8667 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8670 * GstWebRTCBin::create-answer:
8671 * @object: the #webrtcbin
8672 * @options: (nullable): create-answer options
8673 * @promise: a #GstPromise which will contain the answer
8675 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
8676 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
8677 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8678 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
8679 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8682 * GstWebRTCBin::set-local-description:
8683 * @object: the #GstWebRTCBin
8684 * @desc: a #GstWebRTCSessionDescription description
8685 * @promise: (nullable): a #GstPromise to be notified when it's set
8687 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
8688 g_signal_new_class_handler ("set-local-description",
8689 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8690 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
8691 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8694 * GstWebRTCBin::set-remote-description:
8695 * @object: the #GstWebRTCBin
8696 * @desc: a #GstWebRTCSessionDescription description
8697 * @promise: (nullable): a #GstPromise to be notified when it's set
8699 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
8700 g_signal_new_class_handler ("set-remote-description",
8701 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8702 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
8703 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8706 * GstWebRTCBin::add-ice-candidate:
8707 * @object: the #webrtcbin
8708 * @mline_index: the index of the media description in the SDP
8709 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
8712 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
8713 g_signal_new_class_handler ("add-ice-candidate",
8714 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8715 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
8716 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8719 * GstWebRTCBin::get-stats:
8720 * @object: the #webrtcbin
8721 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
8722 * @promise: a #GstPromise for the result
8724 * The @promise will contain the result of retrieving the session statistics.
8725 * The structure will be named 'application/x-webrtc-stats and contain the
8726 * following based on the webrtc-stats spec available from
8727 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
8728 * and is constantly changing these statistics may be changed to fit with
8731 * Each field key is a unique identifier for each RTCStats
8732 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
8733 * GstStructure) in the RTCStatsReport
8734 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
8735 * field in the RTCStats subclass is outlined below.
8737 * Each statistics structure contains the following values as defined by
8738 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
8740 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
8741 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
8742 * "id" G_TYPE_STRING unique identifier
8744 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
8746 * "payload-type" G_TYPE_UINT the rtp payload number in use
8747 * "clock-rate" G_TYPE_UINT the rtp clock-rate
8749 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
8751 * "ssrc" G_TYPE_STRING the rtp sequence src in use
8752 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
8753 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
8754 * "kind" G_TYPE_STRING either "audio" or "video", depending on the associated transceiver (Since: 1.22)
8756 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
8758 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
8759 * "packets-lost" G_TYPE_INT64 number of packets lost
8760 * "packets-discarded" G_TYPE_UINT64 number of packets discarded
8761 * "packets-repaired" G_TYPE_UINT64 number of packets repaired
8762 * "jitter" G_TYPE_DOUBLE packet jitter measured in seconds
8764 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
8766 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
8767 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
8768 * "packets-duplicated" G_TYPE_UINT64 number of packets duplicated
8769 * "fir-count" G_TYPE_UINT FIR packets sent by the receiver
8770 * "pli-count" G_TYPE_UINT PLI packets sent by the receiver
8771 * "nack-count" G_TYPE_UINT NACK packets sent by the receiver
8773 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
8775 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
8776 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
8777 * "fraction-lost" G_TYPE_DOUBLE fraction packet loss
8779 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
8781 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
8782 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
8784 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
8786 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats (optional since 1.22)
8787 * "fir-count" G_TYPE_UINT FIR packets received by the sender
8788 * "pli-count" G_TYPE_UINT PLI packets received by the sender
8789 * "nack-count" G_TYPE_UINT NACK packets received by the sender
8791 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
8793 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
8794 * "remote-timestamp" G_TYPE_DOUBLE remote timestamp the statistics were sent by the remote
8796 * RTCIceCandidateStats supported fields (https://www.w3.org/TR/webrtc-stats/#icecandidate-dict*) (Since: 1.22)
8798 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
8799 * "address" G_TYPE_STRING address of the candidate, allowing for IPv4, IPv6 and FQDNs
8800 * "port" G_TYPE_UINT port number of the candidate
8801 * "candidate-type" G_TYPE_STRING RTCIceCandidateType
8802 * "priority" G_TYPE_UINT64 calculated as defined in RFC 5245
8803 * "protocol" G_TYPE_STRING Either "udp" or "tcp". Based on the "transport" defined in RFC 5245
8804 * "relay-protocol" G_TYPE_STRING protocol used by the endpoint to communicate with the TURN server. Only present for local candidates. Either "udp", "tcp" or "tls"
8805 * "url" G_TYPE_STRING URL of the ICE server from which the candidate was obtained. Only present for local candidates
8807 * RTCIceCandidatePairStats supported fields (https://www.w3.org/TR/webrtc-stats/#candidatepair-dict*) (Since: 1.22)
8809 * "local-candidate-id" G_TYPE_STRING unique identifier that is associated to the object that was inspected to produce the RTCIceCandidateStats for the local candidate associated with this candidate pair.
8810 * "remote-candidate-id" G_TYPE_STRING unique identifier that is associated to the object that was inspected to produce the RTCIceCandidateStats for the remote candidate associated with this candidate pair.
8812 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
8813 g_signal_new_class_handler ("get-stats",
8814 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8815 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
8816 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
8819 * GstWebRTCBin::on-negotiation-needed:
8820 * @object: the #webrtcbin
8822 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
8823 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
8824 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
8827 * GstWebRTCBin::on-ice-candidate:
8828 * @object: the #webrtcbin
8829 * @mline_index: the index of the media description in the SDP
8830 * @candidate: the ICE candidate
8832 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
8833 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
8834 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8835 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8838 * GstWebRTCBin::on-new-transceiver:
8839 * @object: the #webrtcbin
8840 * @candidate: the new #GstWebRTCRTPTransceiver
8842 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
8843 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
8844 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8845 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
8848 * GstWebRTCBin::on-data-channel:
8849 * @object: the #GstWebRTCBin
8850 * @channel: the new `GstWebRTCDataChannel`
8852 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
8853 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
8854 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8855 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
8858 * GstWebRTCBin::prepare-data-channel:
8859 * @object: the #GstWebRTCBin
8860 * @channel: the new `GstWebRTCDataChannel`
8861 * @is_local: Whether this channel is local or remote
8863 * Allows data-channel consumers to configure signal handlers on a newly
8864 * created data-channel, before any data or state change has been notified.
8868 gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL] =
8869 g_signal_new ("prepare-data-channel", G_TYPE_FROM_CLASS (klass),
8870 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2,
8871 GST_TYPE_WEBRTC_DATA_CHANNEL, G_TYPE_BOOLEAN);
8874 * GstWebRTCBin::request-aux-sender:
8875 * @object: the #GstWebRTCBin
8876 * @dtls-transport: The #GstWebRTCDTLSTransport object for which the aux
8877 * sender will be used.
8879 * Request an AUX sender element for the given @dtls-transport.
8881 * Returns: (transfer full): A new GStreamer element
8885 gst_webrtc_bin_signals[REQUEST_AUX_SENDER] =
8886 g_signal_new ("request-aux-sender", G_TYPE_FROM_CLASS (klass),
8887 G_SIGNAL_RUN_LAST, 0, _gst_element_accumulator, NULL, NULL,
8888 GST_TYPE_ELEMENT, 1, GST_TYPE_WEBRTC_DTLS_TRANSPORT);
8891 * GstWebRTCBin::add-transceiver:
8892 * @object: the #webrtcbin
8893 * @direction: the direction of the new transceiver
8894 * @caps: (allow none): the codec preferences for this transceiver
8896 * Returns: the new #GstWebRTCRTPTransceiver
8898 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
8899 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
8900 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8901 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
8902 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
8903 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
8906 * GstWebRTCBin::get-transceivers:
8907 * @object: the #webrtcbin
8909 * Returns: a #GArray of #GstWebRTCRTPTransceivers
8911 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
8912 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
8913 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8914 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
8918 * GstWebRTCBin::get-transceiver:
8919 * @object: the #GstWebRTCBin
8920 * @idx: The index of the transceiver
8922 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
8925 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
8926 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
8927 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8928 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
8929 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
8932 * GstWebRTCBin::add-turn-server:
8933 * @object: the #GstWebRTCBin
8934 * @uri: The uri of the server of the form turn(s)://username:password@host:port
8936 * Add a turn server to obtain ICE candidates from
8938 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
8939 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
8940 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8941 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
8942 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
8945 * GstWebRTCBin::create-data-channel:
8946 * @object: the #GstWebRTCBin
8947 * @label: the label for the data channel
8948 * @options: a #GstStructure of options for creating the data channel
8950 * The options dictionary is the same format as the RTCDataChannelInit
8951 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
8952 * and reproduced below
8954 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
8955 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
8956 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
8957 * protocol G_TYPE_STRING The subprotocol used by this channel
8958 * negotiated G_TYPE_BOOLEAN Whether the created data channel should not perform in-band chnanel announcement. If %TRUE, then application must negotiate the channel itself and create the corresponding channel on the peer with the same id.
8959 * id G_TYPE_INT Override the default identifier selection of this channel
8960 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
8962 * Returns: (transfer full): a new data channel object
8964 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
8965 g_signal_new_class_handler ("create-data-channel",
8966 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8967 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
8968 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
8970 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
8971 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SINK_PAD, 0);
8972 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SRC_PAD, 0);
8976 _unparent_and_unref (GObject * object)
8978 GstObject *obj = GST_OBJECT (object);
8980 GST_OBJECT_PARENT (obj) = NULL;
8982 gst_object_unref (obj);
8986 _transport_free (GObject * object)
8988 TransportStream *stream = (TransportStream *) object;
8989 GstWebRTCBin *webrtc;
8991 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
8993 if (stream->transport) {
8994 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
8995 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
8998 gst_object_unref (object);
9002 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
9004 /* Set SINK/SRC flags as webrtcbin can act as one depending on the
9005 * SDP later. Without setting this here already, surrounding bins might not
9006 * notice this and the pipeline configuration might become inconsistent,
9007 * e.g. with regards to latency.
9008 * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
9010 gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
9011 GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
9012 GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
9014 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
9015 g_mutex_init (PC_GET_LOCK (webrtc));
9016 g_cond_init (PC_GET_COND (webrtc));
9018 g_mutex_init (ICE_GET_LOCK (webrtc));
9019 g_mutex_init (DC_GET_LOCK (webrtc));
9021 webrtc->rtpbin = _create_rtpbin (webrtc);
9022 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
9024 webrtc->priv->transceivers =
9025 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
9026 webrtc->priv->transports =
9027 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
9029 webrtc->priv->data_channels =
9030 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
9032 webrtc->priv->pending_data_channels =
9033 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
9035 webrtc->priv->ice_stream_map =
9036 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
9037 webrtc->priv->pending_remote_ice_candidates =
9038 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
9039 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
9040 (GDestroyNotify) _clear_ice_candidate_item);
9042 webrtc->priv->pending_local_ice_candidates =
9043 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
9044 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
9045 (GDestroyNotify) _clear_ice_candidate_item);
9047 /* we start off closed until we move to READY */
9048 webrtc->priv->is_closed = TRUE;
9049 webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;