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"
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
70 * SECTION: element-webrtcbin
73 * This webrtcbin implements the majority of the W3's peerconnection API and
74 * implementation guide where possible. Generating offers, answers and setting
75 * local and remote SDP's are all supported. Both media descriptions and
76 * descriptions involving data channels are supported.
78 * Each input/output pad is equivalent to a Track in W3 parlance which are
79 * added/removed from the bin. The number of requested sink pads is the number
80 * of streams that will be sent to the receiver and will be associated with a
81 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
83 * On the receiving side, RTPTransceiver's are created in response to setting
84 * a remote description. Output pads for the receiving streams in the set
85 * description are also created when data is received.
87 * A TransportStream is created when needed in order to transport the data over
88 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
89 * on the negotiated SDP's between the peers based on the bundle and rtcp
90 * configuration. Some cases are outlined below for a simple single
91 * audio/video/data session:
93 * - max-bundle uses a single transport for all
94 * media/data transported. Renegotiation involves adding/removing the
95 * necessary streams to the existing transports.
96 * - max-compat involves two TransportStream per media stream
97 * to transport the rtp and the rtcp packets and a single TransportStream for
98 * all data channels. Each stream change involves modifying the associated
99 * TransportStream/s as necessary.
104 * assert sending payload type matches the stream
105 * reconfiguration (of anything)
107 * balanced bundle policy
108 * setting custom DTLS certificates
110 * separate session id's from mlineindex properly
111 * how to deal with replacing a input/output track/stream
114 static void _update_need_negotiation (GstWebRTCBin * webrtc);
115 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
116 GstWebRTCBinPad * pad);
119 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
120 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
122 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
125 GST_STATIC_CAPS ("application/x-rtp"));
127 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
130 GST_STATIC_CAPS ("application/x-rtp"));
134 PROP_PAD_TRANSCEIVER = 1,
138 _have_nice_elements (GstWebRTCBin * webrtc)
140 GstPluginFeature *feature;
142 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
144 gst_object_unref (feature);
146 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
147 ("%s", "libnice elements are not available"));
151 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
153 gst_object_unref (feature);
155 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
156 ("%s", "libnice elements are not available"));
164 _have_sctp_elements (GstWebRTCBin * webrtc)
166 GstPluginFeature *feature;
168 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
170 gst_object_unref (feature);
172 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
173 ("%s", "sctp elements are not available"));
177 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
179 gst_object_unref (feature);
181 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
182 ("%s", "sctp elements are not available"));
190 _have_dtls_elements (GstWebRTCBin * webrtc)
192 GstPluginFeature *feature;
194 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
196 gst_object_unref (feature);
198 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
199 ("%s", "dtls elements are not available"));
203 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
205 gst_object_unref (feature);
207 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
208 ("%s", "dtls elements are not available"));
215 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
218 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
219 GValue * value, GParamSpec * pspec)
221 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
224 case PROP_PAD_TRANSCEIVER:
225 g_value_set_object (value, pad->trans);
228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234 gst_webrtc_bin_pad_finalize (GObject * object)
236 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
239 gst_object_unref (pad->trans);
242 if (pad->received_caps)
243 gst_caps_unref (pad->received_caps);
244 pad->received_caps = NULL;
246 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
250 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
252 GObjectClass *gobject_class = (GObjectClass *) klass;
254 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
255 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
257 g_object_class_install_property (gobject_class,
258 PROP_PAD_TRANSCEIVER,
259 g_param_spec_object ("transceiver", "Transceiver",
260 "Transceiver associated with this pad",
261 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
262 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
266 gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
268 if (wpad->received_caps) {
269 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
270 GstPad *pad = GST_PAD (wpad);
272 gst_event_take (&trans->ssrc_event,
273 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
274 gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
275 trans->current_ssrc, NULL)));
277 gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
282 _get_pending_sink_transceiver (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
286 for (ret = webrtc->priv->pending_sink_transceivers; ret; ret = ret->next) {
287 if (ret->data == pad)
295 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
297 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
298 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
299 gboolean check_negotiation = FALSE;
301 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
304 gst_event_parse_caps (event, &caps);
305 check_negotiation = (!wpad->received_caps
306 || !gst_caps_is_equal (wpad->received_caps, caps));
307 gst_caps_replace (&wpad->received_caps, caps);
309 GST_DEBUG_OBJECT (parent,
310 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
311 GST_PTR_FORMAT, pad, check_negotiation, caps);
313 if (check_negotiation) {
314 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
315 const GstStructure *s;
317 s = gst_caps_get_structure (caps, 0);
318 gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
319 gst_webrtc_bin_pad_update_ssrc_event (wpad);
322 /* A remote description might have been set while the pad hadn't
323 * yet received caps, delaying the connection of the input stream
327 GST_OBJECT_LOCK (wpad->trans);
328 if (wpad->trans->current_direction ==
329 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY
330 || wpad->trans->current_direction ==
331 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
332 GList *pending = _get_pending_sink_transceiver (webrtc, wpad);
335 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
336 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
337 wpad->trans, wpad->received_caps);
338 _connect_input_stream (webrtc, wpad);
339 gst_pad_remove_probe (GST_PAD (pad), wpad->block_id);
341 gst_object_unref (pending->data);
342 webrtc->priv->pending_sink_transceivers =
343 g_list_delete_link (webrtc->priv->pending_sink_transceivers,
347 GST_OBJECT_UNLOCK (wpad->trans);
350 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
351 check_negotiation = TRUE;
354 if (check_negotiation) {
356 _update_need_negotiation (webrtc);
360 return gst_pad_event_default (pad, parent, event);
364 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
366 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
367 gboolean ret = FALSE;
369 switch (GST_QUERY_TYPE (query)) {
370 case GST_QUERY_ACCEPT_CAPS:
371 GST_OBJECT_LOCK (wpad->trans);
372 if (wpad->trans->codec_preferences) {
375 gst_query_parse_accept_caps (query, &caps);
377 gst_query_set_accept_caps_result (query,
378 gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
381 GST_OBJECT_UNLOCK (wpad->trans);
386 GstCaps *codec_preferences = NULL;
388 GST_OBJECT_LOCK (wpad->trans);
389 if (wpad->trans->codec_preferences)
390 codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
391 GST_OBJECT_UNLOCK (wpad->trans);
393 if (codec_preferences) {
394 GstCaps *filter = NULL;
395 GstCaps *filter_prefs = NULL;
398 gst_query_parse_caps (query, &filter);
401 filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
402 GST_CAPS_INTERSECT_FIRST);
403 gst_caps_unref (codec_preferences);
405 filter_prefs = codec_preferences;
408 target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
412 result = gst_pad_query_caps (target, filter_prefs);
413 gst_query_set_caps_result (query, result);
414 gst_caps_unref (result);
416 gst_object_unref (target);
418 gst_query_set_caps_result (query, filter_prefs);
421 gst_caps_unref (filter_prefs);
433 return gst_pad_query_default (pad, parent, query);
438 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
442 static GstWebRTCBinPad *
443 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
445 GstWebRTCBinPad *pad;
446 GstPadTemplate *template;
448 if (direction == GST_PAD_SINK)
449 template = gst_static_pad_template_get (&sink_template);
450 else if (direction == GST_PAD_SRC)
451 template = gst_static_pad_template_get (&src_template);
453 g_assert_not_reached ();
456 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
457 direction, "template", template, NULL);
458 gst_object_unref (template);
460 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
461 gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
463 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
464 direction == GST_PAD_SRC ? "src" : "sink");
468 #define gst_webrtc_bin_parent_class parent_class
469 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
470 G_ADD_PRIVATE (GstWebRTCBin)
471 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
472 "webrtcbin element"););
478 CREATE_ANSWER_SIGNAL,
479 SET_LOCAL_DESCRIPTION_SIGNAL,
480 SET_REMOTE_DESCRIPTION_SIGNAL,
481 ADD_ICE_CANDIDATE_SIGNAL,
482 ON_NEGOTIATION_NEEDED_SIGNAL,
483 ON_ICE_CANDIDATE_SIGNAL,
484 ON_NEW_TRANSCEIVER_SIGNAL,
486 ADD_TRANSCEIVER_SIGNAL,
487 GET_TRANSCEIVER_SIGNAL,
488 GET_TRANSCEIVERS_SIGNAL,
489 ADD_TURN_SERVER_SIGNAL,
490 CREATE_DATA_CHANNEL_SIGNAL,
491 ON_DATA_CHANNEL_SIGNAL,
498 PROP_CONNECTION_STATE,
499 PROP_SIGNALING_STATE,
500 PROP_ICE_GATHERING_STATE,
501 PROP_ICE_CONNECTION_STATE,
502 PROP_LOCAL_DESCRIPTION,
503 PROP_CURRENT_LOCAL_DESCRIPTION,
504 PROP_PENDING_LOCAL_DESCRIPTION,
505 PROP_REMOTE_DESCRIPTION,
506 PROP_CURRENT_REMOTE_DESCRIPTION,
507 PROP_PENDING_REMOTE_DESCRIPTION,
511 PROP_ICE_TRANSPORT_POLICY,
517 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
522 GstWebRTCICEStream *stream;
525 /* FIXME: locking? */
527 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
531 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
532 IceStreamItem *item =
533 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
535 if (item->session_id == session_id) {
536 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
537 "session %u", item->stream, session_id);
542 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
548 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
549 GstWebRTCICEStream * stream)
551 IceStreamItem item = { session_id, stream };
553 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
554 "session %u", stream, session_id);
555 g_array_append_val (webrtc->priv->ice_stream_map, item);
558 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
561 static GstWebRTCRTPTransceiver *
562 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
563 FindTransceiverFunc func)
567 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
568 GstWebRTCRTPTransceiver *transceiver =
569 g_ptr_array_index (webrtc->priv->transceivers, i);
571 if (func (transceiver, data))
579 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
581 return g_strcmp0 (trans->mid, mid) == 0;
585 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
590 return trans->mline == *mline;
593 static GstWebRTCRTPTransceiver *
594 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
596 GstWebRTCRTPTransceiver *trans;
598 trans = _find_transceiver (webrtc, &mlineindex,
599 (FindTransceiverFunc) transceiver_match_for_mline);
601 GST_TRACE_OBJECT (webrtc,
602 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
608 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
611 static TransportStream *
612 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
613 FindTransportFunc func)
617 for (i = 0; i < webrtc->priv->transports->len; i++) {
618 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
620 if (func (stream, data))
628 match_stream_for_session (TransportStream * trans, guint * session)
630 return trans->session_id == *session;
633 static TransportStream *
634 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
636 TransportStream *stream;
638 stream = _find_transport (webrtc, &session_id,
639 (FindTransportFunc) match_stream_for_session);
641 GST_TRACE_OBJECT (webrtc,
642 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
647 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
649 static GstWebRTCBinPad *
650 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
652 GstElement *element = GST_ELEMENT (webrtc);
655 GST_OBJECT_LOCK (webrtc);
657 for (; l; l = g_list_next (l)) {
658 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
660 if (func (l->data, data)) {
661 gst_object_ref (l->data);
662 GST_OBJECT_UNLOCK (webrtc);
667 l = webrtc->priv->pending_pads;
668 for (; l; l = g_list_next (l)) {
669 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
671 if (func (l->data, data)) {
672 gst_object_ref (l->data);
673 GST_OBJECT_UNLOCK (webrtc);
677 GST_OBJECT_UNLOCK (webrtc);
682 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
685 static WebRTCDataChannel *
686 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
687 FindDataChannelFunc func)
691 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
692 WebRTCDataChannel *channel =
693 g_ptr_array_index (webrtc->priv->data_channels, i);
695 if (func (channel, data))
703 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
705 return channel->parent.id == *id;
708 /* always called with dc_lock held */
709 static WebRTCDataChannel *
710 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
712 WebRTCDataChannel *channel;
714 channel = _find_data_channel (webrtc, &id,
715 (FindDataChannelFunc) data_channel_match_for_id);
717 GST_TRACE_OBJECT (webrtc,
718 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
724 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
726 GST_OBJECT_LOCK (webrtc);
727 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
728 GST_OBJECT_UNLOCK (webrtc);
732 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
734 GST_OBJECT_LOCK (webrtc);
735 webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
736 GST_OBJECT_UNLOCK (webrtc);
740 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
742 _remove_pending_pad (webrtc, pad);
744 if (webrtc->priv->running)
745 gst_pad_set_active (GST_PAD (pad), TRUE);
746 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
750 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
752 _remove_pending_pad (webrtc, pad);
754 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
759 GstPadDirection direction;
764 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
766 return GST_PAD_DIRECTION (pad) == match->direction
767 && pad->trans->mline == match->mline;
770 static GstWebRTCBinPad *
771 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
774 MLineMatch m = { direction, mline };
776 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
781 GstPadDirection direction;
782 GstWebRTCRTPTransceiver *trans;
786 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
788 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
791 static GstWebRTCBinPad *
792 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
793 GstWebRTCRTPTransceiver * trans)
795 TransMatch m = { direction, trans };
797 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
802 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
804 return pad->ssrc == *ssrc;
808 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
815 _unlock_pc_thread (GMutex * lock)
817 g_mutex_unlock (lock);
818 return G_SOURCE_REMOVE;
822 _gst_pc_thread (GstWebRTCBin * webrtc)
825 webrtc->priv->main_context = g_main_context_new ();
826 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
828 PC_COND_BROADCAST (webrtc);
829 g_main_context_invoke (webrtc->priv->main_context,
830 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
832 /* Having the thread be the thread default GMainContext will break the
833 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
835 g_main_loop_run (webrtc->priv->loop);
837 GST_OBJECT_LOCK (webrtc);
838 g_main_context_unref (webrtc->priv->main_context);
839 webrtc->priv->main_context = NULL;
840 GST_OBJECT_UNLOCK (webrtc);
843 g_main_loop_unref (webrtc->priv->loop);
844 webrtc->priv->loop = NULL;
845 PC_COND_BROADCAST (webrtc);
852 _start_thread (GstWebRTCBin * webrtc)
857 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
858 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
862 while (!webrtc->priv->loop)
863 PC_COND_WAIT (webrtc);
864 webrtc->priv->is_closed = FALSE;
869 _stop_thread (GstWebRTCBin * webrtc)
871 GST_OBJECT_LOCK (webrtc);
872 webrtc->priv->is_closed = TRUE;
873 GST_OBJECT_UNLOCK (webrtc);
876 g_main_loop_quit (webrtc->priv->loop);
877 while (webrtc->priv->loop)
878 PC_COND_WAIT (webrtc);
881 g_thread_unref (webrtc->priv->thread);
885 _execute_op (GstWebRTCBinTask * op)
889 PC_LOCK (op->webrtc);
890 if (op->webrtc->priv->is_closed) {
891 PC_UNLOCK (op->webrtc);
895 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
896 "webrtcbin is closed. aborting execution.");
897 GstStructure *s = gst_structure_new ("application/x-gst-promise",
898 "error", G_TYPE_ERROR, error, NULL);
900 gst_promise_reply (op->promise, s);
902 g_clear_error (&error);
904 GST_DEBUG_OBJECT (op->webrtc,
905 "Peerconnection is closed, aborting execution");
909 s = op->op (op->webrtc, op->data);
911 PC_UNLOCK (op->webrtc);
914 gst_promise_reply (op->promise, s);
916 gst_structure_free (s);
919 return G_SOURCE_REMOVE;
923 _free_op (GstWebRTCBinTask * op)
926 op->notify (op->data);
928 gst_promise_unref (op->promise);
933 * @promise is for correctly signalling the failure case to the caller when
934 * the user supplies it. Without passing it in, the promise would never
935 * be replied to in the case that @webrtc becomes closed between the idle
936 * source addition and the the execution of the idle source.
939 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
940 gpointer data, GDestroyNotify notify, GstPromise * promise)
942 GstWebRTCBinTask *op;
946 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
948 GST_OBJECT_LOCK (webrtc);
949 if (webrtc->priv->is_closed) {
950 GST_OBJECT_UNLOCK (webrtc);
951 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
956 ctx = g_main_context_ref (webrtc->priv->main_context);
957 GST_OBJECT_UNLOCK (webrtc);
959 op = g_new0 (GstWebRTCBinTask, 1);
965 op->promise = gst_promise_ref (promise);
967 source = g_idle_source_new ();
968 g_source_set_priority (source, G_PRIORITY_DEFAULT);
969 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
970 (GDestroyNotify) _free_op);
971 g_source_attach (source, ctx);
972 g_source_unref (source);
973 g_main_context_unref (ctx);
978 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
979 static GstWebRTCICEConnectionState
980 _collate_ice_connection_states (GstWebRTCBin * webrtc)
982 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
983 GstWebRTCICEConnectionState any_state = 0;
984 gboolean all_new_or_closed = TRUE;
985 gboolean all_completed_or_closed = TRUE;
986 gboolean all_connected_completed_or_closed = TRUE;
989 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
990 GstWebRTCRTPTransceiver *rtp_trans =
991 g_ptr_array_index (webrtc->priv->transceivers, i);
992 GstWebRTCICETransport *transport;
993 GstWebRTCICEConnectionState ice_state;
995 if (rtp_trans->stopped) {
996 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1000 if (!rtp_trans->mid) {
1001 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1005 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
1007 /* get transport state */
1008 g_object_get (transport, "state", &ice_state, NULL);
1009 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
1011 any_state |= (1 << ice_state);
1013 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
1014 all_new_or_closed = FALSE;
1015 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
1016 all_completed_or_closed = FALSE;
1017 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
1018 && ice_state != STATE (CLOSED))
1019 all_connected_completed_or_closed = FALSE;
1022 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
1024 if (webrtc->priv->is_closed) {
1025 GST_TRACE_OBJECT (webrtc, "returning closed");
1026 return STATE (CLOSED);
1028 /* Any of the RTCIceTransports are in the failed state. */
1029 if (any_state & (1 << STATE (FAILED))) {
1030 GST_TRACE_OBJECT (webrtc, "returning failed");
1031 return STATE (FAILED);
1033 /* Any of the RTCIceTransports are in the disconnected state. */
1034 if (any_state & (1 << STATE (DISCONNECTED))) {
1035 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1036 return STATE (DISCONNECTED);
1038 /* All of the RTCIceTransports are in the new or closed state, or there are
1040 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
1041 GST_TRACE_OBJECT (webrtc, "returning new");
1044 /* Any of the RTCIceTransports are in the checking or new state. */
1045 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
1046 GST_TRACE_OBJECT (webrtc, "returning checking");
1047 return STATE (CHECKING);
1049 /* All RTCIceTransports are in the completed or closed state. */
1050 if (all_completed_or_closed) {
1051 GST_TRACE_OBJECT (webrtc, "returning completed");
1052 return STATE (COMPLETED);
1054 /* All RTCIceTransports are in the connected, completed or closed state. */
1055 if (all_connected_completed_or_closed) {
1056 GST_TRACE_OBJECT (webrtc, "returning connected");
1057 return STATE (CONNECTED);
1060 GST_FIXME ("unspecified situation, returning old state");
1061 return webrtc->ice_connection_state;
1065 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1066 static GstWebRTCICEGatheringState
1067 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1069 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1070 GstWebRTCICEGatheringState any_state = 0;
1071 gboolean all_completed = webrtc->priv->transceivers->len > 0;
1074 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1075 GstWebRTCRTPTransceiver *rtp_trans =
1076 g_ptr_array_index (webrtc->priv->transceivers, i);
1077 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1078 TransportStream *stream = trans->stream;
1079 GstWebRTCDTLSTransport *dtls_transport;
1080 GstWebRTCICETransport *transport;
1081 GstWebRTCICEGatheringState ice_state;
1083 if (rtp_trans->stopped || stream == NULL) {
1084 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1089 /* We only have a mid in the transceiver after we got the SDP answer,
1090 * which is usually long after gathering has finished */
1091 if (!rtp_trans->mid) {
1092 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1095 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1096 if (dtls_transport == NULL) {
1097 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1101 transport = dtls_transport->transport;
1103 /* get gathering state */
1104 g_object_get (transport, "gathering-state", &ice_state, NULL);
1105 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1107 any_state |= (1 << ice_state);
1108 if (ice_state != STATE (COMPLETE))
1109 all_completed = FALSE;
1112 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1114 /* Any of the RTCIceTransport s are in the gathering state. */
1115 if (any_state & (1 << STATE (GATHERING))) {
1116 GST_TRACE_OBJECT (webrtc, "returning gathering");
1117 return STATE (GATHERING);
1119 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1120 * the completed gathering state. */
1121 if (all_completed) {
1122 GST_TRACE_OBJECT (webrtc, "returning complete");
1123 return STATE (COMPLETE);
1126 /* Any of the RTCIceTransport s are in the new gathering state and none
1127 * of the transports are in the gathering state, or there are no transports. */
1128 GST_TRACE_OBJECT (webrtc, "returning new");
1133 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1134 static GstWebRTCPeerConnectionState
1135 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1137 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1138 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1139 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1140 GstWebRTCICEConnectionState any_ice_state = 0;
1141 GstWebRTCDTLSTransportState any_dtls_state = 0;
1142 gboolean ice_all_new_or_closed = TRUE;
1143 gboolean dtls_all_new_or_closed = TRUE;
1144 gboolean ice_all_new_connecting_or_checking = TRUE;
1145 gboolean dtls_all_new_connecting_or_checking = TRUE;
1146 gboolean ice_all_connected_completed_or_closed = TRUE;
1147 gboolean dtls_all_connected_completed_or_closed = TRUE;
1150 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1151 GstWebRTCRTPTransceiver *rtp_trans =
1152 g_ptr_array_index (webrtc->priv->transceivers, i);
1153 GstWebRTCDTLSTransport *transport;
1154 GstWebRTCICEConnectionState ice_state;
1155 GstWebRTCDTLSTransportState dtls_state;
1157 if (rtp_trans->stopped) {
1158 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1161 if (!rtp_trans->mid) {
1162 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1166 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1168 /* get transport state */
1169 g_object_get (transport, "state", &dtls_state, NULL);
1170 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1172 any_dtls_state |= (1 << dtls_state);
1174 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1175 dtls_all_new_or_closed = FALSE;
1176 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1177 dtls_all_new_connecting_or_checking = FALSE;
1178 if (dtls_state != DTLS_STATE (CONNECTED)
1179 && dtls_state != DTLS_STATE (CLOSED))
1180 dtls_all_connected_completed_or_closed = FALSE;
1182 g_object_get (transport->transport, "state", &ice_state, NULL);
1183 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1185 any_ice_state |= (1 << ice_state);
1187 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1188 ice_all_new_or_closed = FALSE;
1189 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1190 ice_all_new_connecting_or_checking = FALSE;
1191 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1192 && ice_state != ICE_STATE (CLOSED))
1193 ice_all_connected_completed_or_closed = FALSE;
1196 // also check data channel transport state
1197 if (webrtc->priv->data_channel_transport) {
1198 GstWebRTCDTLSTransport *transport =
1199 webrtc->priv->data_channel_transport->transport;
1200 GstWebRTCICEConnectionState ice_state;
1201 GstWebRTCDTLSTransportState dtls_state;
1203 g_object_get (transport, "state", &dtls_state, NULL);
1204 GST_TRACE_OBJECT (webrtc, "data channel transport DTLS state: 0x%x",
1206 any_dtls_state |= (1 << dtls_state);
1208 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1209 dtls_all_new_or_closed = FALSE;
1210 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1211 dtls_all_new_connecting_or_checking = FALSE;
1212 if (dtls_state != DTLS_STATE (CONNECTED)
1213 && dtls_state != DTLS_STATE (CLOSED))
1214 dtls_all_connected_completed_or_closed = FALSE;
1216 g_object_get (transport->transport, "state", &ice_state, NULL);
1217 GST_TRACE_OBJECT (webrtc, "data channel transport ICE state: 0x%x",
1219 any_ice_state |= (1 << ice_state);
1221 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1222 ice_all_new_or_closed = FALSE;
1223 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1224 ice_all_new_connecting_or_checking = FALSE;
1225 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1226 && ice_state != ICE_STATE (CLOSED))
1227 ice_all_connected_completed_or_closed = FALSE;
1230 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1231 "state: 0x%x", any_ice_state, any_dtls_state);
1233 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1234 if (webrtc->priv->is_closed) {
1235 GST_TRACE_OBJECT (webrtc, "returning closed");
1236 return STATE (CLOSED);
1239 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1240 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1241 GST_TRACE_OBJECT (webrtc, "returning failed");
1242 return STATE (FAILED);
1244 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1245 GST_TRACE_OBJECT (webrtc, "returning failed");
1246 return STATE (FAILED);
1249 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1251 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1252 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1253 return STATE (DISCONNECTED);
1256 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1257 * state, or there are no transports. */
1258 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1259 || webrtc->priv->transports->len == 0) {
1260 GST_TRACE_OBJECT (webrtc, "returning new");
1264 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1265 * or checking state. */
1266 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1267 GST_TRACE_OBJECT (webrtc, "returning connecting");
1268 return STATE (CONNECTING);
1271 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1272 * completed or closed state. */
1273 if (dtls_all_connected_completed_or_closed
1274 && ice_all_connected_completed_or_closed) {
1275 GST_TRACE_OBJECT (webrtc, "returning connected");
1276 return STATE (CONNECTED);
1279 /* FIXME: Unspecified state that happens for us */
1280 if ((dtls_all_new_connecting_or_checking
1281 || dtls_all_connected_completed_or_closed)
1282 && (ice_all_new_connecting_or_checking
1283 || ice_all_connected_completed_or_closed)) {
1284 GST_TRACE_OBJECT (webrtc, "returning connecting");
1285 return STATE (CONNECTING);
1288 GST_FIXME_OBJECT (webrtc,
1289 "Undefined situation detected, returning old state");
1290 return webrtc->peer_connection_state;
1296 static GstStructure *
1297 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1299 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1300 GstWebRTCICEGatheringState new_state;
1302 new_state = _collate_ice_gathering_states (webrtc);
1304 /* If the new state is complete, before we update the public state,
1305 * check if anyone published more ICE candidates while we were collating
1306 * and stop if so, because it means there's a new later
1307 * ice_gathering_state_task queued */
1308 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1310 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1311 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1312 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1314 ICE_UNLOCK (webrtc);
1317 if (new_state != webrtc->ice_gathering_state) {
1318 gchar *old_s, *new_s;
1320 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1322 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1324 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1325 old_s, old_state, new_s, new_state);
1329 webrtc->ice_gathering_state = new_state;
1331 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1339 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1341 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1345 static GstStructure *
1346 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1348 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1349 GstWebRTCICEConnectionState new_state;
1351 new_state = _collate_ice_connection_states (webrtc);
1353 if (new_state != old_state) {
1354 gchar *old_s, *new_s;
1356 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1358 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1360 GST_INFO_OBJECT (webrtc,
1361 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1366 webrtc->ice_connection_state = new_state;
1368 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1376 _update_ice_connection_state (GstWebRTCBin * webrtc)
1378 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1382 static GstStructure *
1383 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1385 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1386 GstWebRTCPeerConnectionState new_state;
1388 new_state = _collate_peer_connection_states (webrtc);
1390 if (new_state != old_state) {
1391 gchar *old_s, *new_s;
1393 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1395 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1397 GST_INFO_OBJECT (webrtc,
1398 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1403 webrtc->peer_connection_state = new_state;
1405 g_object_notify (G_OBJECT (webrtc), "connection-state");
1413 _update_peer_connection_state (GstWebRTCBin * webrtc)
1415 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1420 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1423 gboolean res = FALSE;
1425 GST_OBJECT_LOCK (webrtc);
1426 l = GST_ELEMENT (webrtc)->pads;
1427 for (; l; l = g_list_next (l)) {
1428 GstWebRTCBinPad *wpad;
1430 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1433 wpad = GST_WEBRTC_BIN_PAD (l->data);
1434 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1435 && (!wpad->trans || !wpad->trans->stopped)) {
1436 if (wpad->trans && wpad->trans->codec_preferences) {
1444 l = webrtc->priv->pending_pads;
1445 for (; l; l = g_list_next (l)) {
1446 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1454 GST_OBJECT_UNLOCK (webrtc);
1458 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1460 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1464 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1466 /* We can't negotiate until we have received caps on all our sink pads,
1467 * as we will need the ssrcs in our offer / answer */
1468 if (!_all_sinks_have_caps (webrtc)) {
1469 GST_LOG_OBJECT (webrtc,
1470 "no negotiation possible until caps have been received on all sink pads");
1474 /* If any implementation-specific negotiation is required, as described at
1475 * the start of this section, return "true".
1477 /* FIXME: emit when input caps/format changes? */
1479 if (!webrtc->current_local_description) {
1480 GST_LOG_OBJECT (webrtc, "no local description set");
1484 if (!webrtc->current_remote_description) {
1485 GST_LOG_OBJECT (webrtc, "no remote description set");
1489 /* If connection has created any RTCDataChannel's, and no m= section has
1490 * been negotiated yet for data, return "true". */
1491 if (webrtc->priv->data_channels->len > 0) {
1492 if (_message_get_datachannel_index (webrtc->current_local_description->
1493 sdp) >= G_MAXUINT) {
1494 GST_LOG_OBJECT (webrtc,
1495 "no data channel media section and have %u " "transports",
1496 webrtc->priv->data_channels->len);
1501 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1502 GstWebRTCRTPTransceiver *trans;
1504 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1506 if (trans->stopped) {
1507 /* FIXME: If t is stopped and is associated with an m= section according to
1508 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1509 * rejected in connection's currentLocalDescription or
1510 * currentRemoteDescription , return "true". */
1511 GST_FIXME_OBJECT (webrtc,
1512 "check if the transceiver is rejected in descriptions");
1514 const GstSDPMedia *media;
1515 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1517 if (trans->mline == -1 || trans->mid == NULL) {
1518 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1519 " mid %s", i, trans, trans->mid);
1522 /* internal inconsistency */
1523 g_assert (trans->mline <
1524 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1525 g_assert (trans->mline <
1526 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1528 /* FIXME: msid handling
1529 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1530 * section in connection's currentLocalDescription doesn't contain an
1531 * "a=msid" line, return "true". */
1534 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1536 local_dir = _get_direction_from_media (media);
1539 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1541 remote_dir = _get_direction_from_media (media);
1543 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1544 /* If connection's currentLocalDescription if of type "offer", and
1545 * the direction of the associated m= section in neither the offer
1546 * nor answer matches t's direction, return "true". */
1548 if (local_dir != trans->direction && remote_dir != trans->direction) {
1549 gchar *local_str, *remote_str, *dir_str;
1552 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1555 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1558 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1561 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1562 "description (local %s remote %s)", dir_str, local_str,
1567 g_free (remote_str);
1571 } else if (webrtc->current_local_description->type ==
1572 GST_WEBRTC_SDP_TYPE_ANSWER) {
1573 GstWebRTCRTPTransceiverDirection intersect_dir;
1575 /* If connection's currentLocalDescription if of type "answer", and
1576 * the direction of the associated m= section in the answer does not
1577 * match t's direction intersected with the offered direction (as
1578 * described in [JSEP] (section 5.3.1.)), return "true". */
1580 /* remote is the offer, local is the answer */
1581 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1583 if (intersect_dir != trans->direction) {
1584 gchar *local_str, *remote_str, *inter_str, *dir_str;
1587 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1590 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1593 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1596 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1599 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1600 "description intersected direction %s (local %s remote %s)",
1601 dir_str, local_str, inter_str, remote_str);
1605 g_free (remote_str);
1614 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1618 static GstStructure *
1619 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1621 if (webrtc->priv->need_negotiation) {
1622 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1624 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1632 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1634 _update_need_negotiation (GstWebRTCBin * webrtc)
1636 /* If connection's [[isClosed]] slot is true, abort these steps. */
1637 if (webrtc->priv->is_closed)
1639 /* If connection's signaling state is not "stable", abort these steps. */
1640 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1643 /* If the result of checking if negotiation is needed is "false", clear the
1644 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1645 * to false, and abort these steps. */
1646 if (!_check_if_negotiation_is_needed (webrtc)) {
1647 webrtc->priv->need_negotiation = FALSE;
1650 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1651 if (webrtc->priv->need_negotiation)
1653 /* Set connection's [[needNegotiation]] slot to true. */
1654 webrtc->priv->need_negotiation = TRUE;
1655 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1656 * true, fire a simple event named negotiationneeded at connection. */
1657 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1662 _query_pad_caps (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * rtp_trans,
1663 GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
1668 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1669 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1671 /* Only return an error if actual empty caps were returned from the query. */
1672 if (gst_caps_is_empty (caps)) {
1673 g_set_error (error, GST_WEBRTC_BIN_ERROR,
1674 GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
1675 "Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
1676 gst_clear_caps (&caps);
1677 gst_caps_unref (filter);
1681 n = gst_caps_get_size (caps);
1683 /* Make sure the caps are complete enough to figure out the media type and
1684 * encoding-name, otherwise they would match with basically any media. */
1685 caps = gst_caps_make_writable (caps);
1686 for (i = n; i > 0; i--) {
1687 const GstStructure *s = gst_caps_get_structure (caps, i - 1);
1689 if (!gst_structure_has_name (s, "application/x-rtp") ||
1690 !gst_structure_has_field (s, "media") ||
1691 !gst_structure_has_field (s, "encoding-name")) {
1692 gst_caps_remove_structure (caps, i - 1);
1697 /* If the filtering above resulted in empty caps, or the caps were ANY to
1698 * begin with, then don't report and error but just NULL.
1700 * This would be the case if negotiation would not fail but the peer does
1701 * not have any specific enough preferred caps that would allow us to
1704 if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
1705 GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
1706 gst_clear_caps (&caps);
1709 gst_caps_unref (filter);
1715 _find_codec_preferences (GstWebRTCBin * webrtc,
1716 GstWebRTCRTPTransceiver * rtp_trans, guint media_idx, GError ** error)
1718 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1719 GstCaps *ret = NULL;
1720 GstCaps *codec_preferences = NULL;
1721 GstWebRTCBinPad *pad = NULL;
1722 GstPadDirection direction;
1724 g_assert (rtp_trans);
1725 g_assert (error && *error == NULL);
1727 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1730 GST_OBJECT_LOCK (rtp_trans);
1731 if (rtp_trans->codec_preferences) {
1732 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1733 rtp_trans->codec_preferences);
1734 codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
1736 GST_OBJECT_UNLOCK (rtp_trans);
1738 if (rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
1739 direction = GST_PAD_SRC;
1741 direction = GST_PAD_SINK;
1743 pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
1745 /* try to find a pad */
1747 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1749 /* For the case where we have set our transceiver to sendrecv, but the
1750 * sink pad has not been requested yet.
1753 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1755 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1757 /* try to find a pad */
1759 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
1763 GstCaps *caps = NULL;
1765 if (pad->received_caps) {
1766 caps = gst_caps_ref (pad->received_caps);
1768 static GstStaticCaps static_filter =
1769 GST_STATIC_CAPS ("application/x-rtp, "
1770 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1771 GstCaps *filter = gst_static_caps_get (&static_filter);
1773 filter = gst_caps_make_writable (filter);
1775 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
1776 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
1777 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
1778 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
1780 caps = _query_pad_caps (webrtc, rtp_trans, pad, filter, error);
1782 gst_object_unref (pad);
1788 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1789 GstWebRTCBinPad *srcpad =
1790 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1793 caps = _query_pad_caps (webrtc, rtp_trans, srcpad, caps, error);
1794 gst_object_unref (srcpad);
1801 if (caps && codec_preferences) {
1802 GstCaps *intersection;
1804 intersection = gst_caps_intersect_full (codec_preferences, caps,
1805 GST_CAPS_INTERSECT_FIRST);
1806 gst_clear_caps (&caps);
1808 if (gst_caps_is_empty (intersection)) {
1809 g_set_error (error, GST_WEBRTC_BIN_ERROR,
1810 GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
1811 "Caps negotiation on pad %s failed againt codec preferences",
1812 GST_PAD_NAME (pad));
1813 gst_clear_caps (&intersection);
1815 caps = intersection;
1821 gst_caps_replace (&trans->last_configured_caps, caps);
1828 if (codec_preferences)
1829 ret = gst_caps_ref (codec_preferences);
1830 else if (trans->last_configured_caps)
1831 ret = gst_caps_ref (trans->last_configured_caps);
1836 if (codec_preferences)
1837 gst_caps_unref (codec_preferences);
1840 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1846 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1847 WebRTCTransceiver * trans, const GstCaps * caps)
1856 ret = gst_caps_make_writable (caps);
1858 kind = webrtc_kind_from_caps (ret);
1859 for (i = 0; i < gst_caps_get_size (ret); i++) {
1860 GstStructure *s = gst_caps_get_structure (ret, i);
1863 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1864 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1866 if (kind == GST_WEBRTC_KIND_VIDEO
1867 && !gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1868 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1869 if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1870 gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
1872 /* FIXME: codec-specific parameters? */
1879 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1880 GParamSpec * pspec, GstWebRTCBin * webrtc)
1882 _update_ice_connection_state (webrtc);
1883 _update_peer_connection_state (webrtc);
1887 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1888 GParamSpec * pspec, GstWebRTCBin * webrtc)
1890 _update_ice_gathering_state (webrtc);
1894 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1895 GParamSpec * pspec, GstWebRTCBin * webrtc)
1897 _update_peer_connection_state (webrtc);
1901 match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
1903 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1905 return (trans->current_ssrc == GPOINTER_TO_UINT (data));
1909 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
1910 gboolean early, gpointer user_data)
1912 GstWebRTCBin *webrtc = user_data;
1913 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
1914 GstRTCPPacket packet;
1916 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
1919 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
1920 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
1922 GstWebRTCRTPTransceiver *rtp_trans;
1923 WebRTCTransceiver *trans;
1925 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
1928 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1930 trans = (WebRTCTransceiver *) rtp_trans;
1932 if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
1934 gchar *pad_name = NULL;
1937 g_strdup_printf ("send_rtcp_src_%u",
1938 rtp_trans->sender->transport->session_id);
1939 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
1942 gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
1943 gst_object_unref (pad);
1949 gst_rtcp_buffer_unmap (&rtcp);
1952 /* False means we don't care about suppression */
1957 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
1959 GObject *internal_session = NULL;
1961 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
1962 session_id, &internal_session);
1964 if (internal_session) {
1965 g_signal_connect (internal_session, "on-sending-rtcp",
1966 G_CALLBACK (_on_sending_rtcp), webrtc);
1967 g_object_unref (internal_session);
1972 weak_free (GWeakRef * weak)
1974 g_weak_ref_clear (weak);
1978 static GstPadProbeReturn
1979 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1981 GstWebRTCBin *webrtc = g_weak_ref_get ((GWeakRef *) user_data);
1984 return GST_PAD_PROBE_REMOVE;
1986 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
1987 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
1988 const GstStructure *s =
1989 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
1991 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
1995 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1996 GstWebRTCRTPTransceiver *rtp_trans;
1998 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
2001 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
2002 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
2003 trans->stream->session_id);
2006 /* Set DSCP field based on
2007 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2009 switch (rtp_trans->sender->priority) {
2010 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2013 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2016 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2017 switch (rtp_trans->kind) {
2018 case GST_WEBRTC_KIND_AUDIO:
2021 case GST_WEBRTC_KIND_VIDEO:
2022 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
2024 case GST_WEBRTC_KIND_UNKNOWN:
2029 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2030 switch (rtp_trans->kind) {
2031 case GST_WEBRTC_KIND_AUDIO:
2034 case GST_WEBRTC_KIND_VIDEO:
2035 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
2037 case GST_WEBRTC_KIND_UNKNOWN:
2044 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
2046 } else if (gst_structure_get_enum (s, "sctp-priority",
2047 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
2050 /* Set DSCP field based on
2051 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2054 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2057 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2060 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2061 dscp = 10; /* AF11 */
2063 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2064 dscp = 18; /* AF21 */
2067 if (webrtc->priv->data_channel_transport)
2068 gst_webrtc_ice_set_tos (webrtc->priv->ice,
2069 webrtc->priv->data_channel_transport->stream, dscp << 2);
2074 gst_object_unref (webrtc);
2076 return GST_PAD_PROBE_OK;
2079 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
2082 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
2084 GstWebRTCPriorityType sctp_priority = 0;
2087 if (!webrtc->priv->sctp_transport)
2091 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2092 GstWebRTCDataChannel *channel
2093 = g_ptr_array_index (webrtc->priv->data_channels, i);
2095 sctp_priority = MAX (sctp_priority, channel->priority);
2099 /* Default priority is low means DSCP field is left as 0 */
2100 if (sctp_priority == 0)
2101 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
2103 /* Nobody asks for DSCP, leave it as-is */
2104 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
2105 !webrtc->priv->tos_attached)
2108 /* If one stream has a non-default priority, then everyone else does too */
2109 gst_webrtc_bin_attach_tos (webrtc);
2111 webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
2116 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
2117 GstWebRTCICETransport * transport)
2122 pad = gst_element_get_static_pad (transport->sink, "sink");
2124 weak = g_new0 (GWeakRef, 1);
2125 g_weak_ref_init (weak, webrtc);
2127 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2128 _nicesink_pad_probe, weak, (GDestroyNotify) weak_free);
2129 gst_object_unref (pad);
2133 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
2137 if (webrtc->priv->tos_attached)
2139 webrtc->priv->tos_attached = TRUE;
2141 for (i = 0; i < webrtc->priv->transports->len; i++) {
2142 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
2144 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
2146 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
2147 stream->transport->transport);
2150 gst_webrtc_bin_update_sctp_priority (webrtc);
2153 static WebRTCTransceiver *
2154 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
2155 GstWebRTCRTPTransceiverDirection direction, guint mline, GstWebRTCKind kind,
2156 GstCaps * codec_preferences)
2158 WebRTCTransceiver *trans;
2159 GstWebRTCRTPTransceiver *rtp_trans;
2160 GstWebRTCRTPSender *sender;
2161 GstWebRTCRTPReceiver *receiver;
2163 sender = gst_webrtc_rtp_sender_new ();
2164 receiver = gst_webrtc_rtp_receiver_new ();
2165 trans = webrtc_transceiver_new (webrtc, sender, receiver);
2166 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2167 rtp_trans->direction = direction;
2168 rtp_trans->mline = mline;
2169 rtp_trans->kind = kind;
2170 rtp_trans->codec_preferences =
2171 codec_preferences ? gst_caps_ref (codec_preferences) : NULL;
2172 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
2173 rtp_trans->stopped = FALSE;
2175 g_signal_connect_object (sender, "notify::priority",
2176 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
2178 g_ptr_array_add (webrtc->priv->transceivers, trans);
2180 gst_object_unref (sender);
2181 gst_object_unref (receiver);
2183 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
2189 static TransportStream *
2190 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2192 GstWebRTCDTLSTransport *transport;
2193 TransportStream *ret;
2196 /* FIXME: how to parametrize the sender and the receiver */
2197 ret = transport_stream_new (webrtc, session_id);
2198 transport = ret->transport;
2200 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2201 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2202 g_signal_connect (G_OBJECT (transport->transport),
2203 "notify::gathering-state",
2204 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2205 g_signal_connect (G_OBJECT (transport), "notify::state",
2206 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2207 if (webrtc->priv->tos_attached)
2208 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2210 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2211 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2212 g_ptr_array_add (webrtc->priv->transports, ret);
2214 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2215 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2216 GST_ELEMENT (webrtc->rtpbin), pad_name))
2217 g_warn_if_reached ();
2220 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2221 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2222 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2223 g_warn_if_reached ();
2226 GST_TRACE_OBJECT (webrtc,
2227 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2232 static TransportStream *
2233 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2235 TransportStream *ret;
2237 ret = _find_transport_for_session (webrtc, session_id);
2240 ret = _create_transport_channel (webrtc, session_id);
2242 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2243 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2248 /* this is called from the webrtc thread with the pc lock held */
2250 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2251 GParamSpec * pspec, GstWebRTCBin * webrtc)
2253 GstWebRTCDataChannelState ready_state;
2255 g_object_get (channel, "ready-state", &ready_state, NULL);
2257 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2261 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2262 if (found == FALSE) {
2263 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2268 g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2271 gst_webrtc_bin_update_sctp_priority (webrtc);
2273 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2275 } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2279 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2280 || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2282 if (found == FALSE) {
2283 GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2290 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2291 GstWebRTCBin * webrtc)
2293 WebRTCDataChannel *channel;
2297 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2301 channel = _find_data_channel_for_id (webrtc, stream_id);
2303 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2304 channel->parent.id = stream_id;
2305 channel->webrtcbin = webrtc;
2307 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
2308 gst_bin_add (GST_BIN (webrtc), channel->appsink);
2310 gst_element_sync_state_with_parent (channel->appsrc);
2311 gst_element_sync_state_with_parent (channel->appsink);
2313 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2315 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2319 g_signal_connect (channel, "notify::ready-state",
2320 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2322 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2323 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2324 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2325 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2326 gst_object_unref (sink_pad);
2330 _on_sctp_state_notify (WebRTCSCTPTransport * sctp, GParamSpec * pspec,
2331 GstWebRTCBin * webrtc)
2333 GstWebRTCSCTPTransportState state;
2335 g_object_get (sctp, "state", &state, NULL);
2337 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2340 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2343 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2344 WebRTCDataChannel *channel;
2346 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2348 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2350 if (!channel->parent.negotiated && !channel->opened)
2351 webrtc_data_channel_start_negotiation (channel);
2357 /* Forward declaration so we can easily disconnect the signal handler */
2358 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2359 GParamSpec * pspec, GstWebRTCBin * webrtc);
2361 static GstStructure *
2362 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2364 TransportStream *stream;
2365 GstWebRTCDTLSTransport *transport;
2366 GstWebRTCDTLSTransportState dtls_state;
2367 WebRTCSCTPTransport *sctp_transport;
2369 stream = webrtc->priv->data_channel_transport;
2370 transport = stream->transport;
2372 g_object_get (transport, "state", &dtls_state, NULL);
2373 /* Not connected yet so just return */
2374 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2375 GST_DEBUG_OBJECT (webrtc,
2376 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2380 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2381 sctp_transport = webrtc->priv->sctp_transport;
2383 /* Not locked state anymore so this was already taken care of before */
2384 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2387 /* Start up the SCTP elements now that the DTLS connection is established */
2388 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2389 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2391 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2392 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2394 if (sctp_transport->sctpdec_block_id) {
2395 GstPad *receive_srcpad;
2398 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2400 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2402 sctp_transport->sctpdec_block_id = 0;
2403 gst_object_unref (receive_srcpad);
2406 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2413 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2414 GParamSpec * pspec, GstWebRTCBin * webrtc)
2416 GstWebRTCDTLSTransportState dtls_state;
2418 g_object_get (transport, "state", &dtls_state, NULL);
2420 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2423 /* Connected now, so schedule a task to update the state of the SCTP
2425 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2426 gst_webrtc_bin_enqueue_task (webrtc,
2427 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2431 static GstPadProbeReturn
2432 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2434 /* Drop all events: we don't care about them and don't want to block on
2435 * them. Sticky events would be forwarded again later once we unblock
2436 * and we don't want to forward them here already because that might
2437 * cause a spurious GST_FLOW_FLUSHING */
2438 if (GST_IS_EVENT (info->data))
2439 return GST_PAD_PROBE_DROP;
2441 /* But block on any actual data-flow so we don't accidentally send that
2442 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2445 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2447 return GST_PAD_PROBE_OK;
2450 static TransportStream *
2451 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2453 if (!webrtc->priv->data_channel_transport) {
2454 TransportStream *stream;
2455 WebRTCSCTPTransport *sctp_transport;
2457 stream = _find_transport_for_session (webrtc, session_id);
2460 stream = _create_transport_channel (webrtc, session_id);
2462 webrtc->priv->data_channel_transport = stream;
2464 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2465 sctp_transport = webrtc_sctp_transport_new ();
2466 sctp_transport->transport =
2467 g_object_ref (webrtc->priv->data_channel_transport->transport);
2468 sctp_transport->webrtcbin = webrtc;
2470 /* Don't automatically start SCTP elements as part of webrtcbin. We
2471 * need to delay this until the DTLS transport is fully connected! */
2472 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2473 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2475 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2476 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2479 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2480 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2481 g_signal_connect (sctp_transport, "notify::state",
2482 G_CALLBACK (_on_sctp_state_notify), webrtc);
2484 if (sctp_transport->sctpdec_block_id == 0) {
2485 GstPad *receive_srcpad;
2487 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2489 sctp_transport->sctpdec_block_id =
2490 gst_pad_add_probe (receive_srcpad,
2491 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2492 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2493 gst_object_unref (receive_srcpad);
2496 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2497 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2498 g_warn_if_reached ();
2500 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2501 GST_ELEMENT (stream->send_bin), "data_sink"))
2502 g_warn_if_reached ();
2504 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2505 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2507 if (!webrtc->priv->sctp_transport) {
2508 /* Connect to the notify::state signal to get notified when the DTLS
2509 * connection is established. Only then can we start the SCTP elements */
2510 g_signal_connect (stream->transport, "notify::state",
2511 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2513 /* As this would be racy otherwise, also schedule a task that checks the
2514 * current state of the connection already without getting the signal
2516 gst_webrtc_bin_enqueue_task (webrtc,
2517 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2520 webrtc->priv->sctp_transport = sctp_transport;
2522 gst_webrtc_bin_update_sctp_priority (webrtc);
2525 return webrtc->priv->data_channel_transport;
2528 static TransportStream *
2529 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2530 gboolean is_datachannel)
2533 return _get_or_create_data_channel_transports (webrtc, session_id);
2535 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2539 g_array_find_uint (GArray * array, guint val)
2543 for (i = 0; i < array->len; i++) {
2544 if (g_array_index (array, guint, i) == val)
2552 _pick_available_pt (GArray * reserved_pts, guint * i)
2554 gboolean ret = FALSE;
2556 for (*i = 96; *i <= 127; (*i)++) {
2557 if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
2558 g_array_append_val (reserved_pts, *i);
2568 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2569 GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
2570 GstSDPMedia * media)
2572 gboolean ret = TRUE;
2574 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2577 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2581 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2584 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2586 str = g_strdup_printf ("%u", pt);
2587 gst_sdp_media_add_format (media, str);
2589 str = g_strdup_printf ("%u red/%d", pt, clockrate);
2590 gst_sdp_media_add_attribute (media, "rtpmap", str);
2593 *rtx_target_pt = pt;
2595 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2598 str = g_strdup_printf ("%u", pt);
2599 gst_sdp_media_add_format (media, str);
2601 str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
2602 gst_sdp_media_add_attribute (media, "rtpmap", str);
2611 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2612 GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
2613 GstSDPMedia * media)
2615 gboolean ret = TRUE;
2617 if (trans->local_rtx_ssrc_map)
2618 gst_structure_free (trans->local_rtx_ssrc_map);
2620 trans->local_rtx_ssrc_map =
2621 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2623 if (trans->do_nack) {
2627 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2630 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2632 str = g_strdup_printf ("%u", target_ssrc);
2633 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2634 g_random_int (), NULL);
2637 str = g_strdup_printf ("%u", pt);
2638 gst_sdp_media_add_format (media, str);
2641 str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
2642 gst_sdp_media_add_attribute (media, "rtpmap", str);
2645 str = g_strdup_printf ("%u apt=%d", pt, target_pt);
2646 gst_sdp_media_add_attribute (media, "fmtp", str);
2654 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2656 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2657 GstSDPMedia * media)
2662 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2663 g_value_get_uint (value));
2664 gst_sdp_media_add_attribute (media, "ssrc-group", str);
2674 GstWebRTCBin *webrtc;
2675 WebRTCTransceiver *trans;
2679 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2685 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2686 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2687 cname = gst_structure_get_string (sdes, "cname");
2689 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2691 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2692 cname, GST_OBJECT_NAME (data->trans));
2693 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2696 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2697 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2700 gst_structure_free (sdes);
2706 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2707 WebRTCTransceiver * trans)
2710 RtxSsrcData data = { media, webrtc, trans };
2714 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2715 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2716 cname = gst_structure_get_string (sdes, "cname");
2718 if (trans->local_rtx_ssrc_map)
2719 gst_structure_foreach (trans->local_rtx_ssrc_map,
2720 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2722 for (i = 0; i < gst_caps_get_size (caps); i++) {
2723 const GstStructure *s = gst_caps_get_structure (caps, i);
2726 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2729 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2731 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2732 GST_OBJECT_NAME (trans));
2733 gst_sdp_media_add_attribute (media, "ssrc", str);
2736 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2737 gst_sdp_media_add_attribute (media, "ssrc", str);
2742 gst_structure_free (sdes);
2744 if (trans->local_rtx_ssrc_map)
2745 gst_structure_foreach (trans->local_rtx_ssrc_map,
2746 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2750 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2751 GstSDPMedia * media)
2753 gchar *cert, *fingerprint, *val;
2755 g_object_get (transport, "certificate", &cert, NULL);
2758 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2761 g_strdup_printf ("%s %s",
2762 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2763 g_free (fingerprint);
2765 gst_sdp_media_add_attribute (media, "fingerprint", val);
2770 _parse_extmap (GQuark field_id, const GValue * value, GError ** error)
2774 if (G_VALUE_HOLDS_STRING (value)) {
2775 ret = g_value_dup_string (value);
2776 } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
2777 && gst_value_array_get_size (value) == 3) {
2779 const gchar *direction, *extensionname, *extensionattributes;
2781 val = gst_value_array_get_value (value, 0);
2782 direction = g_value_get_string (val);
2784 val = gst_value_array_get_value (value, 1);
2785 extensionname = g_value_get_string (val);
2787 val = gst_value_array_get_value (value, 2);
2788 extensionattributes = g_value_get_string (val);
2790 if (!extensionname || *extensionname == '\0')
2793 if (direction && *direction != '\0' && extensionattributes
2794 && *extensionattributes != '\0') {
2796 g_strdup_printf ("/%s %s %s", direction, extensionname,
2797 extensionattributes);
2798 } else if (direction && *direction != '\0') {
2799 ret = g_strdup_printf ("/%s %s", direction, extensionname);
2800 } else if (extensionattributes && *extensionattributes != '\0') {
2801 ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
2803 ret = g_strdup (extensionname);
2807 if (!ret && error) {
2808 gchar *val_str = gst_value_serialize (value);
2810 g_set_error (error, GST_WEBRTC_BIN_ERROR,
2811 GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
2812 "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
2823 GstStructure *extmap;
2828 _dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
2830 gboolean is_extmap =
2831 g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
2837 gchar *new_value = _parse_extmap (field_id, value, data->error);
2844 if (gst_structure_id_has_field (data->extmap, field_id)) {
2846 _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
2849 g_assert (old_value);
2851 if (g_strcmp0 (new_value, old_value)) {
2853 ("extmap contains different values for id %s (%s != %s)",
2854 g_quark_to_string (field_id), old_value, new_value);
2855 g_set_error (data->error, GST_WEBRTC_BIN_ERROR,
2856 GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
2857 "extmap contains different values for id %s (%s != %s)",
2858 g_quark_to_string (field_id), old_value, new_value);
2867 gst_structure_id_set_value (data->extmap, field_id, value);
2877 static GstStructure *
2878 _gather_extmap (GstCaps * caps, GError ** error)
2881 { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
2884 n = gst_caps_get_size (caps);
2886 for (i = 0; i < n; i++) {
2887 GstStructure *s = gst_caps_get_structure (caps, i);
2889 gst_structure_filter_and_map_in_place (s,
2890 (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
2893 gst_clear_structure (&edata.extmap);
2898 return edata.extmap;
2902 _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
2904 gst_structure_id_set_value (s, field_id, value);
2909 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2911 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2912 GstWebRTCRTPTransceiver * trans, guint media_idx,
2913 GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2914 gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids,
2918 * rtp header extensions
2925 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2927 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2928 gchar *direction, *sdp_mid, *ufrag, *pwd;
2929 gboolean bundle_only;
2931 GstStructure *extmap;
2934 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2935 || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2938 g_assert (trans->mline == -1 || trans->mline == media_idx);
2940 bundle_only = bundled_mids && bundle_idx != media_idx
2941 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2943 /* mandated by JSEP */
2944 gst_sdp_media_add_attribute (media, "setup", "actpass");
2946 /* FIXME: deal with ICE restarts */
2947 if (last_offer && trans->mline != -1 && trans->mid) {
2948 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2949 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2950 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2952 GST_DEBUG_OBJECT (trans,
2953 "%u Generating new ice parameters mline %i, mid %s", media_idx,
2954 trans->mline, trans->mid);
2955 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2956 _generate_ice_credentials (&ufrag, &pwd);
2958 g_assert (bundle_ufrag && bundle_pwd);
2959 ufrag = g_strdup (bundle_ufrag);
2960 pwd = g_strdup (bundle_pwd);
2964 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2965 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2969 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2970 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2971 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2974 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2977 /* FIXME: negotiate this */
2978 /* FIXME: when bundle_only, these should not be added:
2979 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2980 * However, this causes incompatibilities with current versions
2981 * of the major browsers */
2982 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2983 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2986 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2988 gst_sdp_media_add_attribute (media, direction, "");
2991 caps = _find_codec_preferences (webrtc, trans, media_idx, error);
2993 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2994 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2996 gst_caps_unref (caps);
3000 caps = gst_caps_make_writable (caps);
3002 /* When an extmap is defined twice for the same ID, firefox complains and
3003 * errors out (chrome is smart enough to accept strict duplicates).
3005 * To work around this, we deduplicate extmap attributes, and also error
3006 * out when a different extmap is defined for the same ID.
3008 * _gather_extmap will strip out all extmap- fields, which will then be
3009 * added upon adding the first format for the media.
3011 extmap = _gather_extmap (caps, error);
3014 GST_ERROR_OBJECT (webrtc,
3015 "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
3016 gst_caps_unref (caps);
3020 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3023 for (i = 0; i < gst_caps_get_size (caps); i++) {
3024 GstCaps *format = gst_caps_new_empty ();
3025 GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
3028 gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
3031 gst_caps_append_structure (format, s);
3033 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
3034 " to %u-th media", i, format, media_idx);
3036 /* this only looks at the first structure so we loop over the given caps
3037 * and add each structure inside it piecemeal */
3038 gst_sdp_media_set_media_from_caps (format, media);
3040 gst_caps_unref (format);
3043 gst_clear_structure (&extmap);
3046 const GstStructure *s = gst_caps_get_structure (caps, 0);
3047 gint clockrate = -1;
3049 gint original_rtx_target_pt; /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
3050 guint rtx_target_ssrc = -1;
3052 if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
3053 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3054 g_array_append_val (reserved_pts, rtx_target_pt);
3056 original_rtx_target_pt = rtx_target_pt;
3058 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
3059 GST_WARNING_OBJECT (webrtc,
3060 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
3061 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
3062 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
3065 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3066 clockrate, &rtx_target_pt, media);
3067 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3068 clockrate, rtx_target_pt, rtx_target_ssrc, media);
3069 if (original_rtx_target_pt != rtx_target_pt)
3070 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3071 clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
3074 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
3076 /* Some identifier; we also add the media name to it so it's identifiable */
3078 gst_sdp_media_add_attribute (media, "mid", trans->mid);
3080 /* Make sure to avoid mid collisions */
3082 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3083 webrtc->priv->media_counter++);
3084 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3087 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3088 g_hash_table_insert (all_mids, sdp_mid, NULL);
3095 * - add a=candidate lines for gathered candidates
3098 if (trans->sender) {
3099 if (!trans->sender->transport) {
3100 TransportStream *item;
3103 _get_or_create_transport_stream (webrtc,
3104 bundled_mids ? bundle_idx : media_idx, FALSE);
3106 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
3109 _add_fingerprint_to_media (trans->sender->transport, media);
3113 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3116 g_string_append_printf (bundled_mids, " %s", mid);
3119 gst_caps_unref (caps);
3125 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
3127 if (pad->received_caps) {
3128 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
3131 if (gst_structure_get_int (s, "payload", &pt)) {
3132 GST_TRACE_OBJECT (pad, "have reserved pt %u from received caps", pt);
3133 g_array_append_val (reserved_pts, pt);
3139 gather_reserved_pts (GstWebRTCBin * webrtc)
3141 GstElement *element = GST_ELEMENT (webrtc);
3142 GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3145 GST_OBJECT_LOCK (webrtc);
3146 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
3147 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
3150 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3151 GstWebRTCRTPTransceiver *trans;
3153 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3154 GST_OBJECT_LOCK (trans);
3155 if (trans->codec_preferences) {
3159 n = gst_caps_get_size (trans->codec_preferences);
3160 for (j = 0; j < n; j++) {
3161 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
3162 if (gst_structure_get_int (s, "payload", &pt)) {
3163 GST_TRACE_OBJECT (trans, "have reserved pt %u from codec preferences",
3165 g_array_append_val (reserved_pts, pt);
3169 GST_OBJECT_UNLOCK (trans);
3171 GST_OBJECT_UNLOCK (webrtc);
3173 return reserved_pts;
3177 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
3178 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
3179 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
3181 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3182 gchar *ufrag, *pwd, *sdp_mid;
3183 gboolean bundle_only = bundled_mids
3184 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
3185 && gst_sdp_message_medias_len (msg) != bundle_idx;
3186 guint last_data_index = G_MAXUINT;
3188 /* add data channel support */
3189 if (webrtc->priv->data_channels->len == 0)
3193 last_data_index = _message_get_datachannel_index (last_offer);
3194 if (last_data_index < G_MAXUINT) {
3195 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
3196 /* XXX: is this always true when recycling transceivers?
3197 * i.e. do we always put the data channel in the same mline */
3198 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
3202 /* mandated by JSEP */
3203 gst_sdp_media_add_attribute (media, "setup", "actpass");
3205 /* FIXME: only needed when restarting ICE */
3206 if (last_offer && last_data_index < G_MAXUINT) {
3207 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
3208 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
3210 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3211 _generate_ice_credentials (&ufrag, &pwd);
3213 ufrag = g_strdup (bundle_ufrag);
3214 pwd = g_strdup (bundle_pwd);
3217 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3218 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3222 gst_sdp_media_set_media (media, "application");
3223 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
3224 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3225 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3226 gst_sdp_media_add_format (media, "webrtc-datachannel");
3228 if (bundle_idx != gst_sdp_message_medias_len (msg))
3229 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3231 if (last_offer && last_data_index < G_MAXUINT) {
3232 const GstSDPMedia *last_data_media;
3235 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
3236 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
3238 gst_sdp_media_add_attribute (media, "mid", mid);
3240 /* Make sure to avoid mid collisions */
3242 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3243 webrtc->priv->media_counter++);
3244 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3247 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3248 g_hash_table_insert (all_mids, sdp_mid, NULL);
3255 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3258 g_string_append_printf (bundled_mids, " %s", mid);
3261 /* FIXME: negotiate this properly */
3262 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3264 _get_or_create_data_channel_transports (webrtc,
3265 bundled_mids ? 0 : webrtc->priv->transceivers->len);
3266 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
3271 /* TODO: use the options argument */
3272 static GstSDPMessage *
3273 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3276 GstSDPMessage *ret = NULL;
3277 GString *bundled_mids = NULL;
3278 gchar *bundle_ufrag = NULL;
3279 gchar *bundle_pwd = NULL;
3280 GArray *reserved_pts = NULL;
3281 GHashTable *all_mids =
3282 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3284 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3285 GList *seen_transceivers = NULL;
3286 guint media_idx = 0;
3289 gst_sdp_message_new (&ret);
3291 gst_sdp_message_set_version (ret, "0");
3294 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
3296 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
3297 sess_id = g_strdup (origin->sess_id);
3299 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
3301 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
3305 gst_sdp_message_set_session_name (ret, "-");
3306 gst_sdp_message_add_time (ret, "0", "0", NULL);
3307 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
3309 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
3310 bundled_mids = g_string_new ("BUNDLE");
3311 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
3312 bundled_mids = g_string_new ("BUNDLE");
3315 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3316 GStrv last_bundle = NULL;
3317 guint bundle_media_index;
3319 reserved_pts = gather_reserved_pts (webrtc);
3320 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
3321 && last_bundle && last_bundle && last_bundle[0]
3322 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
3324 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
3326 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
3328 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3331 g_strfreev (last_bundle);
3334 /* FIXME: recycle transceivers */
3336 /* Fill up the renegotiated streams first */
3338 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
3339 GstWebRTCRTPTransceiver *trans = NULL;
3340 const GstSDPMedia *last_media;
3342 last_media = gst_sdp_message_get_media (last_offer, i);
3344 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
3345 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
3346 const gchar *last_mid;
3348 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
3350 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3351 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3353 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
3356 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
3358 g_assert (!g_list_find (seen_transceivers, trans));
3360 if (wtrans->mline_locked && trans->mline != media_idx) {
3361 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3362 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3363 "Previous negotiatied transceiver %"
3364 GST_PTR_FORMAT " with mid %s was in mline %d but transceiver"
3365 " has locked mline %u", trans, trans->mid, media_idx,
3370 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3371 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3372 trans->mid, media_idx);
3374 /* FIXME: deal with format changes */
3375 gst_sdp_media_copy (last_media, &media);
3376 _media_replace_direction (media, trans->direction);
3378 mid = gst_sdp_media_get_attribute_val (media, "mid");
3381 if (g_hash_table_contains (all_mids, mid)) {
3382 gst_sdp_media_free (media);
3383 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3384 GST_WEBRTC_BIN_ERROR_FAILED,
3385 "Duplicate mid %s when creating offer", mid);
3389 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3392 g_string_append_printf (bundled_mids, " %s", mid);
3394 gst_sdp_message_add_media (ret, media);
3397 gst_sdp_media_free (media);
3398 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3402 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3403 "application") == 0) {
3404 GstSDPMedia media = { 0, };
3405 gst_sdp_media_init (&media);
3406 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3407 bundle_ufrag, bundle_pwd, all_mids)) {
3408 gst_sdp_message_add_media (ret, &media);
3411 gst_sdp_media_uninit (&media);
3417 /* First, go over all transceivers and gather existing mids */
3418 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3419 GstWebRTCRTPTransceiver *trans;
3421 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3423 if (g_list_find (seen_transceivers, trans))
3427 if (g_hash_table_contains (all_mids, trans->mid)) {
3428 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
3429 "Duplicate mid %s when creating offer", trans->mid);
3433 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3438 /* add any extra streams */
3440 GstWebRTCRTPTransceiver *trans = NULL;
3441 GstSDPMedia media = { 0, };
3443 /* First find a transceiver requesting this m-line */
3444 trans = _find_transceiver_for_mline (webrtc, media_idx);
3447 /* We can't have seen it already, because it is locked to this line */
3448 g_assert (!g_list_find (seen_transceivers, trans));
3449 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3451 /* Otherwise find a free transceiver */
3452 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3453 WebRTCTransceiver *wtrans;
3455 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3456 wtrans = WEBRTC_TRANSCEIVER (trans);
3458 /* don't add transceivers twice */
3459 if (g_list_find (seen_transceivers, trans))
3462 /* Ignore transceivers with a locked mline, as they would have been
3463 * found above or will be used later */
3464 if (wtrans->mline_locked)
3467 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3468 /* don't add stopped transceivers */
3469 if (trans->stopped) {
3473 /* Otherwise take it */
3477 /* Stop if we got all transceivers */
3478 if (i == webrtc->priv->transceivers->len) {
3480 /* But try to add a data channel first, we do it here, because
3481 * it can allow a locked m-line to be put after, so we need to
3482 * do another iteration after.
3484 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3485 GstSDPMedia media = { 0, };
3486 gst_sdp_media_init (&media);
3487 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3488 bundle_ufrag, bundle_pwd, all_mids)) {
3489 gst_sdp_message_add_media (ret, &media);
3493 gst_sdp_media_uninit (&media);
3497 /* Verify that we didn't ignore any locked m-line transceivers */
3498 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3499 WebRTCTransceiver *wtrans;
3501 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3502 wtrans = WEBRTC_TRANSCEIVER (trans);
3503 /* don't add transceivers twice */
3504 if (g_list_find (seen_transceivers, trans))
3506 g_assert (wtrans->mline_locked);
3508 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3509 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3510 "Tranceiver %" GST_PTR_FORMAT " with mid %s has locked mline %d"
3511 " but the whole offer only has %u sections", trans, trans->mid,
3512 trans->mline, media_idx);
3519 gst_sdp_media_init (&media);
3521 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3522 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3525 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3526 "index %u", trans, media_idx);
3528 if (sdp_media_from_transceiver (webrtc, &media, trans, media_idx,
3529 bundled_mids, 0, bundle_ufrag, bundle_pwd, reserved_pts, all_mids,
3531 /* as per JSEP, a=rtcp-mux-only is only added for new streams */
3532 gst_sdp_media_add_attribute (&media, "rtcp-mux-only", "");
3533 gst_sdp_message_add_media (ret, &media);
3536 gst_sdp_media_uninit (&media);
3539 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3540 g_array_free (reserved_pts, TRUE);
3541 reserved_pts = NULL;
3547 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3548 g_array_free (reserved_pts, TRUE);
3549 reserved_pts = NULL;
3552 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
3555 g_assert (media_idx == gst_sdp_message_medias_len (ret));
3558 gchar *mids = g_string_free (bundled_mids, FALSE);
3560 gst_sdp_message_add_attribute (ret, "group", mids);
3562 bundled_mids = NULL;
3565 /* FIXME: pre-emptively setup receiving elements when needed */
3567 if (webrtc->priv->last_generated_answer)
3568 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3569 webrtc->priv->last_generated_answer = NULL;
3570 if (webrtc->priv->last_generated_offer)
3571 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3573 GstSDPMessage *copy;
3574 gst_sdp_message_copy (ret, ©);
3575 webrtc->priv->last_generated_offer =
3576 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3581 g_array_free (reserved_pts, TRUE);
3583 g_hash_table_unref (all_mids);
3585 g_list_free (seen_transceivers);
3588 g_free (bundle_ufrag);
3591 g_free (bundle_pwd);
3594 g_string_free (bundled_mids, TRUE);
3599 gst_sdp_message_free (ret);
3605 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3606 gint * rtx_target_pt)
3610 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3613 for (i = 0; i < gst_caps_get_size (caps); i++) {
3614 const GstStructure *s = gst_caps_get_structure (caps, i);
3616 if (gst_structure_has_name (s, "application/x-rtp")) {
3617 const gchar *encoding_name =
3618 gst_structure_get_string (s, "encoding-name");
3622 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3623 gst_structure_get_int (s, "payload", &pt)) {
3624 if (!g_strcmp0 (encoding_name, "RED")) {
3627 str = g_strdup_printf ("%u", pt);
3628 gst_sdp_media_add_format (media, str);
3630 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3631 *rtx_target_pt = pt;
3632 gst_sdp_media_add_attribute (media, "rtpmap", str);
3634 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3637 str = g_strdup_printf ("%u", pt);
3638 gst_sdp_media_add_format (media, str);
3640 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3641 gst_sdp_media_add_attribute (media, "rtpmap", str);
3650 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3651 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3654 const GstStructure *s;
3656 if (trans->local_rtx_ssrc_map)
3657 gst_structure_free (trans->local_rtx_ssrc_map);
3659 trans->local_rtx_ssrc_map =
3660 gst_structure_new_empty ("application/x-rtp-ssrc-map");
3662 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
3663 s = gst_caps_get_structure (offer_caps, i);
3665 if (gst_structure_has_name (s, "application/x-rtp")) {
3666 const gchar *encoding_name =
3667 gst_structure_get_string (s, "encoding-name");
3668 const gchar *apt_str = gst_structure_get_string (s, "apt");
3676 apt = atoi (apt_str);
3678 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3679 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
3680 if (!g_strcmp0 (encoding_name, "RTX")) {
3683 str = g_strdup_printf ("%u", pt);
3684 gst_sdp_media_add_format (media, str);
3686 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
3687 gst_sdp_media_add_attribute (media, "rtpmap", str);
3690 str = g_strdup_printf ("%d apt=%d", pt, apt);
3691 gst_sdp_media_add_attribute (media, "fmtp", str);
3694 str = g_strdup_printf ("%u", target_ssrc);
3695 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
3696 g_random_int (), NULL);
3704 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
3705 const GstCaps * caps)
3707 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
3709 if (trans->kind == kind)
3712 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
3721 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
3722 guint * target_ssrc)
3724 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
3726 gst_structure_get_int (s, "payload", target_pt);
3727 gst_structure_get_uint (s, "ssrc", target_ssrc);
3730 /* TODO: use the options argument */
3731 static GstSDPMessage *
3732 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3735 GstSDPMessage *ret = NULL;
3736 const GstWebRTCSessionDescription *pending_remote =
3737 webrtc->pending_remote_description;
3739 GStrv bundled = NULL;
3740 guint bundle_idx = 0;
3741 GString *bundled_mids = NULL;
3742 gchar *bundle_ufrag = NULL;
3743 gchar *bundle_pwd = NULL;
3744 GList *seen_transceivers = NULL;
3745 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
3747 if (!webrtc->pending_remote_description) {
3748 g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
3749 GST_WEBRTC_BIN_ERROR_INVALID_STATE,
3750 "Asked to create an answer without a remote description");
3754 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
3758 GStrv last_bundle = NULL;
3759 guint bundle_media_index;
3761 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
3762 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
3763 "Bundle tag is %s but no media found matching", bundled[0]);
3767 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3768 bundled_mids = g_string_new ("BUNDLE");
3771 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
3772 && last_bundle && last_bundle[0]
3773 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
3775 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
3777 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
3779 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3782 g_strfreev (last_bundle);
3785 gst_sdp_message_new (&ret);
3787 gst_sdp_message_set_version (ret, "0");
3789 const GstSDPOrigin *offer_origin =
3790 gst_sdp_message_get_origin (pending_remote->sdp);
3791 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
3792 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
3794 gst_sdp_message_set_session_name (ret, "-");
3796 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
3797 const GstSDPAttribute *attr =
3798 gst_sdp_message_get_attribute (pending_remote->sdp, i);
3800 if (g_strcmp0 (attr->key, "ice-options") == 0) {
3801 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
3805 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
3806 GstSDPMedia *media = NULL;
3807 GstSDPMedia *offer_media;
3808 GstWebRTCDTLSSetup offer_setup, answer_setup;
3810 gboolean bundle_only;
3814 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
3815 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
3817 gst_sdp_media_new (&media);
3818 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3819 gst_sdp_media_set_port_info (media, 0, 0);
3821 gst_sdp_media_set_port_info (media, 9, 0);
3822 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3827 /* FIXME: deal with ICE restarts */
3828 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
3829 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
3830 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
3833 _generate_ice_credentials (&ufrag, &pwd);
3835 ufrag = g_strdup (bundle_ufrag);
3836 pwd = g_strdup (bundle_pwd);
3839 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3840 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3845 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
3846 const GstSDPAttribute *attr =
3847 gst_sdp_media_get_attribute (offer_media, j);
3849 if (g_strcmp0 (attr->key, "mid") == 0
3850 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
3851 gst_sdp_media_add_attribute (media, attr->key, attr->value);
3852 /* FIXME: handle anything we want to keep */
3856 mid = gst_sdp_media_get_attribute_val (media, "mid");
3857 /* XXX: not strictly required but a lot of functionality requires a mid */
3860 /* set the a=setup: attribute */
3861 offer_setup = _get_dtls_setup_from_media (offer_media);
3862 answer_setup = _intersect_dtls_setup (offer_setup);
3863 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
3864 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
3865 "transceiver direction");
3868 _media_replace_setup (media, answer_setup);
3870 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
3873 if (gst_sdp_media_formats_len (offer_media) != 1) {
3874 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
3875 "for webrtc-datachannel");
3878 sctp_port = _get_sctp_port_from_media (offer_media);
3879 if (sctp_port == -1) {
3880 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
3884 /* XXX: older browsers will produce a different SDP format for data
3885 * channel that is currently not parsed correctly */
3886 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3888 gst_sdp_media_set_media (media, "application");
3889 gst_sdp_media_set_port_info (media, 9, 0);
3890 gst_sdp_media_add_format (media, "webrtc-datachannel");
3892 /* FIXME: negotiate this properly on renegotiation */
3893 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3895 _get_or_create_data_channel_transports (webrtc,
3896 bundled_mids ? bundle_idx : i);
3900 g_string_append_printf (bundled_mids, " %s", mid);
3903 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
3905 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
3906 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
3907 GstCaps *offer_caps, *answer_caps = NULL;
3908 GstWebRTCRTPTransceiver *rtp_trans = NULL;
3909 WebRTCTransceiver *trans = NULL;
3910 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
3911 gint target_pt = -1;
3912 gint original_target_pt = -1;
3913 guint target_ssrc = 0;
3915 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3916 offer_caps = _rtp_caps_from_media (offer_media);
3918 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
3920 _find_transceiver (webrtc, mid,
3921 (FindTransceiverFunc) match_for_mid))) {
3922 const GstSDPMedia *last_media =
3923 gst_sdp_message_get_media (last_answer, i);
3924 const gchar *last_mid =
3925 gst_sdp_media_get_attribute_val (last_media, "mid");
3926 GstCaps *current_caps;
3928 /* FIXME: assumes no shenanigans with recycling transceivers */
3929 g_assert (g_strcmp0 (mid, last_mid) == 0);
3931 current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
3933 gst_caps_unref (offer_caps);
3937 current_caps = _rtp_caps_from_media (last_media);
3940 answer_caps = gst_caps_intersect (offer_caps, current_caps);
3941 if (gst_caps_is_empty (answer_caps)) {
3942 GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
3943 GST_PTR_FORMAT ") don't intersect with caps from codec"
3944 " preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
3946 gst_caps_unref (current_caps);
3947 gst_caps_unref (answer_caps);
3948 gst_caps_unref (offer_caps);
3951 gst_caps_unref (current_caps);
3954 /* XXX: In theory we're meant to use the sendrecv formats for the
3955 * inactive direction however we don't know what that may be and would
3956 * require asking outside what it expects to possibly send later */
3958 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
3959 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
3960 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
3962 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3963 GstCaps *trans_caps;
3965 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3967 if (g_list_find (seen_transceivers, rtp_trans)) {
3968 /* Don't double allocate a transceiver to multiple mlines */
3973 trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
3975 gst_caps_unref (offer_caps);
3979 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
3980 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
3982 /* FIXME: technically this is a little overreaching as some fields we
3983 * we can deal with not having and/or we may have unrecognized fields
3984 * that we cannot actually support */
3986 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
3987 gst_caps_unref (trans_caps);
3989 if (!gst_caps_is_empty (answer_caps)) {
3990 GST_LOG_OBJECT (webrtc,
3991 "found compatible transceiver %" GST_PTR_FORMAT
3992 " for offer media %u", rtp_trans, i);
3995 gst_caps_unref (answer_caps);
4004 answer_dir = rtp_trans->direction;
4005 g_assert (answer_caps != NULL);
4007 /* if no transceiver, then we only receive that stream and respond with
4008 * the intersection with the transceivers codec preferences caps */
4009 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
4013 GstCaps *trans_caps;
4014 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
4016 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
4017 kind = GST_WEBRTC_KIND_AUDIO;
4018 else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
4020 kind = GST_WEBRTC_KIND_VIDEO;
4022 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
4023 GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
4025 trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
4026 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4028 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
4029 " for mline %u with media kind %d", trans, i, kind);
4031 trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4033 gst_caps_unref (offer_caps);
4037 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4038 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4040 /* FIXME: technically this is a little overreaching as some fields we
4041 * we can deal with not having and/or we may have unrecognized fields
4042 * that we cannot actually support */
4044 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4045 gst_caps_unref (trans_caps);
4047 answer_caps = gst_caps_ref (offer_caps);
4050 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4053 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
4055 if (gst_caps_is_empty (answer_caps)) {
4056 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
4057 gst_caps_unref (answer_caps);
4058 gst_caps_unref (offer_caps);
4062 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps))
4063 GST_WARNING_OBJECT (webrtc,
4064 "Trying to change transceiver %d kind from %d to %d",
4065 rtp_trans->mline, rtp_trans->kind,
4066 webrtc_kind_from_caps (answer_caps));
4068 if (!trans->do_nack) {
4069 answer_caps = gst_caps_make_writable (answer_caps);
4070 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
4071 GstStructure *s = gst_caps_get_structure (answer_caps, k);
4072 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
4076 gst_sdp_media_set_media_from_caps (answer_caps, media);
4078 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
4081 original_target_pt = target_pt;
4083 _media_add_fec (media, trans, offer_caps, &target_pt);
4084 if (trans->do_nack) {
4085 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
4086 if (target_pt != original_target_pt)
4087 _media_add_rtx (media, trans, offer_caps, original_target_pt,
4091 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
4092 _media_add_ssrcs (media, answer_caps, webrtc,
4093 WEBRTC_TRANSCEIVER (rtp_trans));
4095 gst_caps_unref (answer_caps);
4098 /* set the new media direction */
4099 offer_dir = _get_direction_from_media (offer_media);
4100 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
4101 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4102 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
4103 "transceiver direction");
4104 gst_caps_unref (offer_caps);
4107 _media_replace_direction (media, answer_dir);
4109 if (!trans->stream) {
4110 TransportStream *item;
4113 _get_or_create_transport_stream (webrtc,
4114 bundled_mids ? bundle_idx : i, FALSE);
4115 webrtc_transceiver_set_transport (trans, item);
4119 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
4122 g_string_append_printf (bundled_mids, " %s", mid);
4125 /* set the a=fingerprint: for this transport */
4126 _add_fingerprint_to_media (trans->stream->transport, media);
4128 gst_caps_unref (offer_caps);
4130 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
4136 if (error && *error)
4137 GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
4139 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
4140 gst_sdp_media_free (media);
4141 gst_sdp_media_copy (offer_media, &media);
4142 gst_sdp_media_set_port_info (media, 0, 0);
4143 /* Clear error here as it is not propagated to the caller and the media
4144 * is just skipped, i.e. more iterations are going to happen. */
4145 g_clear_error (error);
4147 gst_sdp_message_add_media (ret, media);
4148 gst_sdp_media_free (media);
4152 gchar *mids = g_string_free (bundled_mids, FALSE);
4154 gst_sdp_message_add_attribute (ret, "group", mids);
4159 g_free (bundle_ufrag);
4162 g_free (bundle_pwd);
4164 /* FIXME: can we add not matched transceivers? */
4166 /* XXX: only true for the initial offerer */
4167 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
4170 g_strfreev (bundled);
4172 g_list_free (seen_transceivers);
4174 if (webrtc->priv->last_generated_offer)
4175 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4176 webrtc->priv->last_generated_offer = NULL;
4177 if (webrtc->priv->last_generated_answer)
4178 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4180 GstSDPMessage *copy;
4181 gst_sdp_message_copy (ret, ©);
4182 webrtc->priv->last_generated_answer =
4183 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
4191 GstStructure *options;
4192 GstWebRTCSDPType type;
4195 static GstStructure *
4196 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
4198 GstWebRTCSessionDescription *desc = NULL;
4199 GstSDPMessage *sdp = NULL;
4200 GstStructure *s = NULL;
4201 GError *error = NULL;
4203 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
4204 gst_webrtc_sdp_type_to_string (data->type), data->options);
4206 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
4207 sdp = _create_offer_task (webrtc, data->options, &error);
4208 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
4209 sdp = _create_answer_task (webrtc, data->options, &error);
4211 g_assert_not_reached ();
4216 desc = gst_webrtc_session_description_new (data->type, sdp);
4217 s = gst_structure_new ("application/x-gst-promise",
4218 gst_webrtc_sdp_type_to_string (data->type),
4219 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
4221 g_warn_if_fail (error != NULL);
4222 GST_WARNING_OBJECT (webrtc, "returning error: %s",
4223 error ? error->message : "Unknown");
4224 s = gst_structure_new ("application/x-gst-promise",
4225 "error", G_TYPE_ERROR, error, NULL);
4226 g_clear_error (&error);
4232 gst_webrtc_session_description_free (desc);
4238 _free_create_sdp_data (struct create_sdp *data)
4241 gst_structure_free (data->options);
4246 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
4247 const GstStructure * options, GstPromise * promise)
4249 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4252 data->options = gst_structure_copy (options);
4253 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
4255 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4256 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4258 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
4259 "Could not create offer. webrtcbin is closed");
4260 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4261 "error", G_TYPE_ERROR, error, NULL);
4263 gst_promise_reply (promise, s);
4265 g_clear_error (&error);
4270 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
4271 const GstStructure * options, GstPromise * promise)
4273 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4276 data->options = gst_structure_copy (options);
4277 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
4279 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4280 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4282 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
4283 "Could not create answer. webrtcbin is closed.");
4284 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4285 "error", G_TYPE_ERROR, error, NULL);
4287 gst_promise_reply (promise, s);
4289 g_clear_error (&error);
4293 static GstWebRTCBinPad *
4294 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
4295 GstWebRTCRTPTransceiver * trans, guint serial)
4297 GstWebRTCBinPad *pad;
4300 if (direction == GST_PAD_SINK) {
4301 if (serial == G_MAXUINT)
4302 serial = webrtc->priv->max_sink_pad_serial++;
4304 serial = trans->mline;
4308 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
4310 pad = gst_webrtc_bin_pad_new (pad_name, direction);
4313 pad->trans = gst_object_ref (trans);
4318 static GstWebRTCRTPTransceiver *
4319 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
4320 const GstSDPMessage * sdp, guint media_idx)
4322 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4323 GstWebRTCRTPTransceiver *ret = NULL;
4326 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4327 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4329 if (g_strcmp0 (attr->key, "mid") == 0) {
4331 _find_transceiver (webrtc, attr->value,
4332 (FindTransceiverFunc) match_for_mid)))
4337 ret = _find_transceiver (webrtc, &media_idx,
4338 (FindTransceiverFunc) transceiver_match_for_mline);
4341 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
4346 _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
4348 GstElement *ret = NULL;
4349 GstElement *prev = NULL;
4350 guint ulpfec_pt = 0;
4352 GstPad *sinkpad = NULL;
4353 GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4355 if (trans->stream) {
4357 transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
4358 red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
4361 if (ulpfec_pt || red_pt)
4362 ret = gst_bin_new (NULL);
4365 GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4366 GstCaps *caps = transport_stream_get_caps_for_pt (trans->stream, ulpfec_pt);
4368 GST_DEBUG_OBJECT (webrtc,
4369 "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
4372 gst_bin_add (GST_BIN (ret), fecenc);
4373 sinkpad = gst_element_get_static_pad (fecenc, "sink");
4374 g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
4375 trans->fec_percentage, NULL);
4377 g_object_bind_property (rtp_trans, "fec-percentage", fecenc, "percentage",
4378 G_BINDING_BIDIRECTIONAL);
4380 if (caps && !gst_caps_is_empty (caps)) {
4381 const GstStructure *s = gst_caps_get_structure (caps, 0);
4382 const gchar *media = gst_structure_get_string (s, "media");
4384 if (!g_strcmp0 (media, "video"))
4385 g_object_set (fecenc, "multipacket", TRUE, NULL);
4392 GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
4394 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
4395 rtp_trans->mline, red_pt);
4397 gst_bin_add (GST_BIN (ret), redenc);
4399 gst_element_link (prev, redenc);
4401 sinkpad = gst_element_get_static_pad (redenc, "sink");
4403 g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
4409 GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
4410 gst_object_unref (sinkpad);
4411 gst_element_add_pad (ret, ghost);
4415 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
4416 GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
4417 gst_object_unref (srcpad);
4418 gst_element_add_pad (ret, ghost);
4426 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
4431 * ,--------------------------------------------webrtcbin--------------------------------------------,
4433 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
4434 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
4435 * ; ,---clocksync---, ; ; ; ; ;
4436 * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
4437 * ; sink_%u ; ; ,---fec encoder---, ; ; '---------------------' ;
4438 * o---------o sink src o-o sink src o--o send_rtp_sink_%u ; ;
4439 * ; '---------------' ,-----------------, '--------------------' ;
4440 * '-------------------------------------------------------------------------------------------------'
4445 * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
4447 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
4448 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
4450 * ; sink_%u ,---clocksync---, ,---fec encoder---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
4451 * o----------o sink src o-o sink src o--o sink_%u ; ; ; '---------------------' ;
4452 * ; '---------------' ,-----------------, ; ; ; ; ;
4453 * ; ; src o-o send_rtp_sink_%u ; ;
4454 * ; sink_%u ,---clocksync---, ,---fec encoder---, ; ; ; ; ;
4455 * o----------o sink src o-o sink src o--o sink%u ; '--------------------' ;
4456 * ; '---------------' ,-----------------, '------------' ;
4457 * '-----------------------------------------------------------------------------------------------------------------'
4459 GstPadTemplate *rtp_templ;
4460 GstPad *rtp_sink, *sinkpad, *srcpad;
4462 WebRTCTransceiver *trans;
4463 GstElement *clocksync;
4464 GstElement *fec_encoder;
4466 g_return_val_if_fail (pad->trans != NULL, NULL);
4468 trans = WEBRTC_TRANSCEIVER (pad->trans);
4470 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
4472 g_assert (trans->stream);
4474 clocksync = gst_element_factory_make ("clocksync", NULL);
4475 g_object_set (clocksync, "sync", TRUE, NULL);
4476 gst_bin_add (GST_BIN (webrtc), clocksync);
4477 gst_element_sync_state_with_parent (clocksync);
4479 srcpad = gst_element_get_static_pad (clocksync, "src");
4480 sinkpad = gst_element_get_static_pad (clocksync, "sink");
4482 if ((fec_encoder = _build_fec_encoder (webrtc, trans))) {
4485 gst_bin_add (GST_BIN (webrtc), fec_encoder);
4486 gst_element_sync_state_with_parent (fec_encoder);
4488 fec_sink = gst_element_get_static_pad (fec_encoder, "sink");
4489 gst_pad_link (srcpad, fec_sink);
4490 gst_object_unref (srcpad);
4491 gst_object_unref (fec_sink);
4492 srcpad = gst_element_get_static_pad (fec_encoder, "src");
4495 if (!webrtc->rtpfunnel) {
4497 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
4498 "send_rtp_sink_%u");
4499 g_assert (rtp_templ);
4501 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
4503 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
4505 gst_pad_link (srcpad, rtp_sink);
4506 gst_object_unref (rtp_sink);
4508 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
4510 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
4511 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4512 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
4513 g_warn_if_reached ();
4516 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
4517 GstPad *funnel_sinkpad =
4518 gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
4520 gst_pad_link (srcpad, funnel_sinkpad);
4521 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
4524 gst_object_unref (funnel_sinkpad);
4527 gst_object_unref (srcpad);
4528 gst_object_unref (sinkpad);
4530 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
4532 return GST_PAD (pad);
4535 /* output pads are receiving elements */
4537 _connect_output_stream (GstWebRTCBin * webrtc,
4538 TransportStream * stream, guint session_id)
4541 * ,------------------------webrtcbin------------------------,
4542 * ; ,---------rtpbin---------, ;
4543 * ; ,-transport_receive_%u--, ; ; ;
4544 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
4546 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
4547 * ; '-----------------------' ; ; ; src_%u
4548 * ; ; recv_rtp_src_%u_%u_%u o--o
4549 * ; '------------------------' ;
4550 * '---------------------------------------------------------'
4554 if (stream->output_connected) {
4555 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
4556 "connected to rtpbin. Not connecting", stream);
4560 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
4561 session_id, stream);
4563 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
4564 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
4565 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
4566 g_warn_if_reached ();
4569 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
4571 /* The webrtcbin src_%u output pads will be created when rtpbin receives
4572 * data on that stream in on_rtpbin_pad_added() */
4574 stream->output_connected = TRUE;
4584 _clear_ice_candidate_item (IceCandidateItem * item)
4586 g_free (item->candidate);
4590 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
4591 gboolean drop_invalid)
4593 GstWebRTCICEStream *stream;
4595 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
4596 if (stream == NULL) {
4598 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
4601 IceCandidateItem new;
4602 new.mlineindex = item->mlineindex;
4603 new.candidate = g_strdup (item->candidate);
4604 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
4607 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
4608 ICE_UNLOCK (webrtc);
4613 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4614 item->mlineindex, item->candidate);
4616 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
4620 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
4621 const GstSDPMedia * media)
4624 GstWebRTCICEStream *stream = NULL;
4626 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
4627 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
4628 if (g_strcmp0 (attr->key, "candidate") == 0) {
4632 stream = _find_ice_stream_for_session (webrtc, mlineindex);
4633 if (stream == NULL) {
4634 GST_DEBUG_OBJECT (webrtc,
4635 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
4639 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
4640 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4641 mlineindex, candidate);
4642 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
4649 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
4650 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
4652 GstSDPMedia *media = NULL;
4654 if (mline_index < sdp->medias->len) {
4655 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
4658 if (media == NULL) {
4659 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
4663 // Add the candidate as an attribute, first stripping off the existing
4664 // candidate: key from the string description
4665 if (strlen (candidate) < 10) {
4666 GST_WARNING_OBJECT (webrtc,
4667 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
4671 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
4675 _filter_sdp_fields (GQuark field_id, const GValue * value,
4676 GstStructure * new_structure)
4678 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
4679 gst_structure_id_set_value (new_structure, field_id, value);
4685 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
4690 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4691 GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4693 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4696 for (i = 0; i < rtx_count; i++) {
4697 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4698 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4699 const gchar *apt = gst_structure_get_string (s, "apt");
4701 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4702 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4705 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4706 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4707 stream->rtxsend, pt_map);
4709 if (stream->rtxreceive)
4710 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4711 if (stream->rtxsend)
4712 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4714 gst_structure_free (pt_map);
4719 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
4720 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
4724 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4727 proto = gst_sdp_media_get_proto (media);
4728 if (proto != NULL) {
4729 /* Parse global SDP attributes once */
4730 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
4731 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
4732 gst_sdp_message_attributes_to_caps (sdp, global_caps);
4733 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
4734 gst_sdp_media_attributes_to_caps (media, global_caps);
4736 len = gst_sdp_media_formats_len (media);
4737 for (i = 0; i < len; i++) {
4738 GstCaps *caps, *outcaps;
4744 pt = atoi (gst_sdp_media_get_format (media, i));
4746 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
4749 caps = gst_sdp_media_get_caps_from_media (media, pt);
4751 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
4755 /* Merge in global caps */
4756 /* Intersect will merge in missing fields to the current caps */
4757 outcaps = gst_caps_intersect (caps, global_caps);
4758 gst_caps_unref (caps);
4760 s = gst_caps_get_structure (outcaps, 0);
4761 gst_structure_set_name (s, "application/x-rtp");
4762 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
4763 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
4765 item.caps = gst_caps_new_empty ();
4767 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
4768 GstStructure *s = gst_caps_get_structure (outcaps, j);
4769 GstStructure *filtered =
4770 gst_structure_new_empty (gst_structure_get_name (s));
4772 gst_structure_foreach (s,
4773 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
4774 gst_caps_append_structure (item.caps, filtered);
4778 item.media_idx = media_idx;
4779 gst_caps_unref (outcaps);
4781 g_array_append_val (stream->ptmap, item);
4784 gst_caps_unref (global_caps);
4789 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
4790 const GstSDPMessage * sdp, guint media_idx,
4791 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
4792 GStrv bundled, guint bundle_idx, GError ** error)
4794 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4795 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
4796 GstWebRTCRTPTransceiverDirection new_dir;
4797 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4798 GstWebRTCDTLSSetup new_setup;
4799 gboolean new_rtcp_rsize;
4800 ReceiveState receive_state = RECEIVE_STATE_UNSET;
4803 rtp_trans->mline = media_idx;
4805 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
4806 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
4807 GST_FIXME_OBJECT (webrtc,
4808 "Updating video transceiver to audio, which isn't fully supported.");
4809 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
4812 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
4813 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
4814 GST_FIXME_OBJECT (webrtc,
4815 "Updating audio transceiver to video, which isn't fully supported.");
4816 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
4819 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4820 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4822 if (g_strcmp0 (attr->key, "mid") == 0) {
4823 g_free (rtp_trans->mid);
4824 rtp_trans->mid = g_strdup (attr->value);
4829 const GstSDPMedia *local_media, *remote_media;
4830 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
4831 GstWebRTCDTLSSetup local_setup, remote_setup;
4834 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4837 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4840 local_setup = _get_dtls_setup_from_media (local_media);
4841 remote_setup = _get_dtls_setup_from_media (remote_media);
4842 new_setup = _get_final_setup (local_setup, remote_setup);
4843 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4844 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4845 "Cannot intersect direction attributes for media %u", media_idx);
4849 local_dir = _get_direction_from_media (local_media);
4850 remote_dir = _get_direction_from_media (remote_media);
4851 new_dir = _get_final_direction (local_dir, remote_dir);
4852 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4853 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4854 "Cannot intersect dtls setup attributes for media %u", media_idx);
4858 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
4859 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
4860 && prev_dir != new_dir) {
4861 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4862 GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
4863 "transceiver direction changes are not implemented. Media %u",
4868 if (!bundled || bundle_idx == media_idx) {
4869 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
4870 && _media_has_attribute_key (remote_media, "rtcp-rsize");
4874 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
4875 media_idx, &session);
4877 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
4878 g_object_unref (session);
4884 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4886 /* Not a bundled stream means this entire transport is inactive,
4887 * so set the receive state to BLOCK below */
4888 stream->active = FALSE;
4889 receive_state = RECEIVE_STATE_BLOCK;
4892 /* If this transceiver is active for sending or receiving,
4893 * we still need receive at least RTCP, so need to unblock
4894 * the receive bin below. */
4895 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
4896 receive_state = RECEIVE_STATE_PASS;
4897 stream->active = TRUE;
4900 if (new_dir != prev_dir) {
4901 gchar *prev_dir_s, *new_dir_s;
4904 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4907 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4910 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
4911 " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
4913 g_free (prev_dir_s);
4918 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4919 GstWebRTCBinPad *pad;
4921 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
4923 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4925 GstPad *peer = gst_pad_get_peer (target);
4927 gst_pad_send_event (peer, gst_event_new_eos ());
4928 gst_object_unref (peer);
4930 gst_object_unref (target);
4932 gst_object_unref (pad);
4935 /* XXX: send eos event up the sink pad as well? */
4938 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
4939 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4940 GstWebRTCBinPad *pad =
4941 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
4943 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
4944 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4945 gst_object_unref (pad);
4947 GST_DEBUG_OBJECT (webrtc,
4948 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
4949 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
4951 _connect_input_stream (webrtc, pad);
4952 _add_pad (webrtc, pad);
4955 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
4956 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4957 GstWebRTCBinPad *pad =
4958 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4960 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
4961 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4962 gst_object_unref (pad);
4964 GST_DEBUG_OBJECT (webrtc,
4965 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
4966 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
4969 if (!trans->stream) {
4970 TransportStream *item;
4973 _get_or_create_transport_stream (webrtc,
4974 bundled ? bundle_idx : media_idx, FALSE);
4975 webrtc_transceiver_set_transport (trans, item);
4978 _connect_output_stream (webrtc, trans->stream,
4979 bundled ? bundle_idx : media_idx);
4980 /* delay adding the pad until rtpbin creates the recv output pad
4981 * to ghost to so queries/events travel through the pipeline correctly
4982 * as soon as the pad is added */
4983 _add_pad_to_list (webrtc, pad);
4988 rtp_trans->mline = media_idx;
4989 rtp_trans->current_direction = new_dir;
4992 if (!bundled || bundle_idx == media_idx) {
4993 if (stream->rtxsend || stream->rtxreceive) {
4994 _set_rtx_ptmap_from_stream (webrtc, stream);
4997 g_object_set (stream, "dtls-client",
4998 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5001 /* Must be after setting the "dtls-client" so that data is not pushed into
5002 * the dtlssrtp elements before the ssl direction has been set which will
5003 * throw SSL errors */
5004 if (receive_state != RECEIVE_STATE_UNSET)
5005 transport_receive_bin_set_receive_state (stream->receive_bin,
5009 /* must be called with the pc lock held */
5011 _generate_data_channel_id (GstWebRTCBin * webrtc)
5014 gint new_id = -1, max_channels = 0;
5016 if (webrtc->priv->sctp_transport) {
5017 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5020 if (max_channels <= 0) {
5021 max_channels = 65534;
5024 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
5027 /* TODO: a better search algorithm */
5029 WebRTCDataChannel *channel;
5033 if (new_id < 0 || new_id >= max_channels) {
5034 /* exhausted id space */
5035 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
5036 "data channel id (max %i)", max_channels);
5040 /* client must generate even ids, server must generate odd ids */
5041 if (new_id % 2 == ! !is_client)
5044 channel = _find_data_channel_for_id (webrtc, new_id);
5053 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
5054 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
5057 const GstSDPMedia *local_media, *remote_media;
5058 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
5059 TransportReceiveBin *receive;
5060 int local_port, remote_port;
5061 guint64 local_max_size, remote_max_size, max_size;
5065 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5068 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5071 local_setup = _get_dtls_setup_from_media (local_media);
5072 remote_setup = _get_dtls_setup_from_media (remote_media);
5073 new_setup = _get_final_setup (local_setup, remote_setup);
5074 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5075 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5076 "Cannot intersect dtls setup for media %u", media_idx);
5080 /* data channel is always rtcp-muxed to avoid generating ICE candidates
5082 g_object_set (stream, "dtls-client",
5083 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5085 local_port = _get_sctp_port_from_media (local_media);
5086 remote_port = _get_sctp_port_from_media (local_media);
5087 if (local_port == -1 || remote_port == -1) {
5088 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5089 "Could not find sctp port for media %u (local %i, remote %i)",
5090 media_idx, local_port, remote_port);
5094 if (0 == (local_max_size =
5095 _get_sctp_max_message_size_from_media (local_media)))
5096 local_max_size = G_MAXUINT64;
5097 if (0 == (remote_max_size =
5098 _get_sctp_max_message_size_from_media (remote_media)))
5099 remote_max_size = G_MAXUINT64;
5100 max_size = MIN (local_max_size, remote_max_size);
5102 webrtc->priv->sctp_transport->max_message_size = max_size;
5105 guint orig_local_port, orig_remote_port;
5107 /* XXX: sctpassociation warns if we are in the wrong state */
5108 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5109 &orig_local_port, NULL);
5111 if (orig_local_port != local_port)
5112 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5115 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5116 &orig_remote_port, NULL);
5117 if (orig_remote_port != remote_port)
5118 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5123 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
5124 WebRTCDataChannel *channel;
5126 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
5128 if (channel->parent.id == -1)
5129 channel->parent.id = _generate_data_channel_id (webrtc);
5130 if (channel->parent.id == -1)
5131 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5132 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5134 if (webrtc->priv->sctp_transport->association_established
5135 && !channel->parent.negotiated && !channel->opened) {
5136 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
5137 webrtc_data_channel_start_negotiation (channel);
5142 stream->active = TRUE;
5144 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
5145 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
5149 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
5152 GstWebRTCKind kind = GPOINTER_TO_INT (data);
5156 if (p1->mline != -1)
5160 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
5167 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
5170 GstPad *queue_srcpad;
5172 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
5177 if (webrtc->rtpfunnel)
5180 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
5181 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
5182 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
5184 queue = gst_element_factory_make ("queue", NULL);
5185 gst_bin_add (GST_BIN (webrtc), queue);
5186 gst_element_sync_state_with_parent (queue);
5188 gst_element_link (webrtc->rtpfunnel, queue);
5190 queue_srcpad = gst_element_get_static_pad (queue, "src");
5192 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
5193 rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
5195 gst_pad_link (queue_srcpad, rtp_sink);
5196 gst_object_unref (queue_srcpad);
5197 gst_object_unref (rtp_sink);
5199 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
5200 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5201 GST_ELEMENT (stream->send_bin), "rtp_sink"))
5202 g_warn_if_reached ();
5210 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
5211 GstWebRTCSessionDescription * sdp, GError ** error)
5214 gboolean ret = FALSE;
5215 GStrv bundled = NULL;
5216 guint bundle_idx = 0;
5217 TransportStream *bundle_stream = NULL;
5219 /* FIXME: With some peers, it's possible we could have
5220 * multiple bundles to deal with, although I've never seen one yet */
5221 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5222 if (!_parse_bundle (sdp->sdp, &bundled, error))
5227 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
5228 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5229 "Bundle tag is %s but no media found matching", bundled[0]);
5233 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
5234 _message_media_is_datachannel (sdp->sdp, bundle_idx));
5235 /* Mark the bundle stream as inactive to start. It will be set to TRUE
5236 * by any bundled mline that is active, and at the end we set the
5237 * receivebin to BLOCK if all mlines were inactive. */
5238 bundle_stream->active = FALSE;
5240 g_array_set_size (bundle_stream->ptmap, 0);
5241 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5242 /* When bundling, we need to do this up front, or else RTX
5243 * parameters aren't set up properly for the bundled streams */
5244 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
5247 _connect_rtpfunnel (webrtc, bundle_idx);
5250 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5251 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5252 TransportStream *stream;
5253 GstWebRTCRTPTransceiver *trans;
5254 guint transport_idx;
5256 /* skip rejected media */
5257 if (gst_sdp_media_get_port (media) == 0)
5261 transport_idx = bundle_idx;
5265 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5267 stream = _get_or_create_transport_stream (webrtc, transport_idx,
5268 _message_media_is_datachannel (sdp->sdp, transport_idx));
5270 /* When bundling, these were all set up above, but when not
5271 * bundling we need to do it now */
5272 g_array_set_size (stream->ptmap, 0);
5273 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
5277 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
5279 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
5280 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5281 "State mismatch. Could not find local transceiver by mline %u", i);
5284 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
5285 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
5286 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
5288 /* No existing transceiver, find an unused one */
5290 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
5291 kind = GST_WEBRTC_KIND_AUDIO;
5292 else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
5293 kind = GST_WEBRTC_KIND_VIDEO;
5295 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
5296 GST_STR_NULL (gst_sdp_media_get_media (media)));
5298 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
5299 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
5302 /* Still no transceiver? Create one */
5303 /* XXX: default to the advertised direction in the sdp for new
5304 * transceivers. The spec doesn't actually say what happens here, only
5305 * that calls to setDirection will change the value. Nothing about
5306 * a default value when the transceiver is created internally */
5308 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
5309 _get_direction_from_media (media), i, kind, NULL);
5310 webrtc_transceiver_set_transport (t, stream);
5311 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
5314 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
5315 trans, bundled, bundle_idx, error);
5316 if (error && *error)
5318 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
5319 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
5321 if (error && *error)
5324 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
5329 if (bundle_stream && bundle_stream->active == FALSE) {
5330 /* No bundled mline marked the bundle as active, so block the receive bin, as
5331 * this bundle is completely inactive */
5332 GST_LOG_OBJECT (webrtc,
5333 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
5334 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
5335 RECEIVE_STATE_BLOCK);
5341 g_strfreev (bundled);
5347 check_transceivers_not_removed (GstWebRTCBin * webrtc,
5348 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
5353 if (gst_sdp_message_medias_len (previous->sdp) >
5354 gst_sdp_message_medias_len (new->sdp))
5361 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
5366 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5367 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5368 GstWebRTCRTPTransceiver *rtp_trans;
5369 WebRTCTransceiver *trans;
5371 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5372 /* only look for matching mid */
5373 if (rtp_trans == NULL)
5376 trans = WEBRTC_TRANSCEIVER (rtp_trans);
5378 /* We only validate the locked mlines for now */
5379 if (!trans->mline_locked)
5382 if (rtp_trans->mline != i) {
5383 g_set_error (error, GST_WEBRTC_BIN_ERROR,
5384 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
5385 "m-line with mid %s is at position %d, but was locked to %d, "
5386 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
5390 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
5391 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
5392 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
5393 g_set_error (error, GST_WEBRTC_BIN_ERROR,
5394 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
5395 "m-line %d was locked to audio, but SDP has %s media", i,
5396 gst_sdp_media_get_media (media));
5400 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
5401 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
5402 g_set_error (error, GST_WEBRTC_BIN_ERROR,
5403 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
5404 "m-line %d was locked to video, but SDP has %s media", i,
5405 gst_sdp_media_get_media (media));
5415 struct set_description
5418 GstWebRTCSessionDescription *sdp;
5421 static GstWebRTCSessionDescription *
5422 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
5423 GstWebRTCSDPType type)
5426 case GST_WEBRTC_SDP_TYPE_OFFER:
5427 case GST_WEBRTC_SDP_TYPE_PRANSWER:
5428 case GST_WEBRTC_SDP_TYPE_ANSWER:
5429 if (source == SDP_LOCAL) {
5430 return webrtc->current_local_description;
5432 return webrtc->current_remote_description;
5434 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
5437 /* other values mean memory corruption/uninitialized! */
5438 g_assert_not_reached ();
5445 /* http://w3c.github.io/webrtc-pc/#set-description */
5446 static GstStructure *
5447 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
5449 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
5450 gboolean signalling_state_changed = FALSE;
5451 GError *error = NULL;
5452 GStrv bundled = NULL;
5453 guint bundle_idx = 0;
5457 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5458 webrtc->signaling_state);
5460 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
5461 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
5462 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
5463 _sdp_source_to_string (sd->source), type_str, state);
5464 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
5470 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
5473 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5474 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
5478 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
5479 g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5480 "Bundle tag is %s but no matching media found", bundled[0]);
5485 if (!check_transceivers_not_removed (webrtc,
5486 get_previous_description (webrtc, sd->source, sd->sdp->type),
5488 g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR,
5489 GST_WEBRTC_BIN_ERROR_BAD_SDP,
5490 "m=lines removed from the SDP. Processing a completely new connection "
5491 "is not currently supported.");
5495 if (!check_locked_mlines (webrtc, sd->sdp, &error))
5498 switch (sd->sdp->type) {
5499 case GST_WEBRTC_SDP_TYPE_OFFER:{
5500 if (sd->source == SDP_LOCAL) {
5501 if (webrtc->pending_local_description)
5502 gst_webrtc_session_description_free
5503 (webrtc->pending_local_description);
5504 webrtc->pending_local_description =
5505 gst_webrtc_session_description_copy (sd->sdp);
5506 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
5508 if (webrtc->pending_remote_description)
5509 gst_webrtc_session_description_free
5510 (webrtc->pending_remote_description);
5511 webrtc->pending_remote_description =
5512 gst_webrtc_session_description_copy (sd->sdp);
5513 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
5517 case GST_WEBRTC_SDP_TYPE_ANSWER:{
5518 if (sd->source == SDP_LOCAL) {
5519 if (webrtc->current_local_description)
5520 gst_webrtc_session_description_free
5521 (webrtc->current_local_description);
5522 webrtc->current_local_description =
5523 gst_webrtc_session_description_copy (sd->sdp);
5525 if (webrtc->current_remote_description)
5526 gst_webrtc_session_description_free
5527 (webrtc->current_remote_description);
5528 webrtc->current_remote_description = webrtc->pending_remote_description;
5529 webrtc->pending_remote_description = NULL;
5531 if (webrtc->current_remote_description)
5532 gst_webrtc_session_description_free
5533 (webrtc->current_remote_description);
5534 webrtc->current_remote_description =
5535 gst_webrtc_session_description_copy (sd->sdp);
5537 if (webrtc->current_local_description)
5538 gst_webrtc_session_description_free
5539 (webrtc->current_local_description);
5540 webrtc->current_local_description = webrtc->pending_local_description;
5541 webrtc->pending_local_description = NULL;
5544 if (webrtc->pending_local_description)
5545 gst_webrtc_session_description_free (webrtc->pending_local_description);
5546 webrtc->pending_local_description = NULL;
5548 if (webrtc->pending_remote_description)
5549 gst_webrtc_session_description_free
5550 (webrtc->pending_remote_description);
5551 webrtc->pending_remote_description = NULL;
5553 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5556 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
5557 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
5558 if (sd->source == SDP_LOCAL) {
5559 if (webrtc->pending_local_description)
5560 gst_webrtc_session_description_free
5561 (webrtc->pending_local_description);
5562 webrtc->pending_local_description = NULL;
5564 if (webrtc->pending_remote_description)
5565 gst_webrtc_session_description_free
5566 (webrtc->pending_remote_description);
5567 webrtc->pending_remote_description = NULL;
5570 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5573 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
5574 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
5575 if (sd->source == SDP_LOCAL) {
5576 if (webrtc->pending_local_description)
5577 gst_webrtc_session_description_free
5578 (webrtc->pending_local_description);
5579 webrtc->pending_local_description =
5580 gst_webrtc_session_description_copy (sd->sdp);
5582 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
5584 if (webrtc->pending_remote_description)
5585 gst_webrtc_session_description_free
5586 (webrtc->pending_remote_description);
5587 webrtc->pending_remote_description =
5588 gst_webrtc_session_description_copy (sd->sdp);
5590 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
5596 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
5598 * If the mid value of an RTCRtpTransceiver was set to a non-null value
5599 * by the RTCSessionDescription that is being rolled back, set the mid
5600 * value of that transceiver to null, as described by [JSEP]
5601 * (section 4.1.7.2.).
5602 * If an RTCRtpTransceiver was created by applying the
5603 * RTCSessionDescription that is being rolled back, and a track has not
5604 * been attached to it via addTrack, remove that transceiver from
5605 * connection's set of transceivers, as described by [JSEP]
5606 * (section 4.1.7.2.).
5607 * Restore the value of connection's [[ sctpTransport]] internal slot
5608 * to its value at the last stable signaling state.
5612 if (webrtc->signaling_state != new_signaling_state) {
5613 webrtc->signaling_state = new_signaling_state;
5614 signalling_state_changed = TRUE;
5618 gboolean ice_controller = FALSE;
5620 /* get the current value so we don't change ice controller from TRUE to
5621 * FALSE on renegotiation or once set to TRUE for the initial local offer */
5622 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
5624 /* we control ice negotiation if we send the initial offer */
5626 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
5627 && webrtc->current_remote_description == NULL;
5628 /* or, if the remote is an ice-lite peer */
5629 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
5630 && webrtc->current_remote_description
5631 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
5634 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
5635 ice_controller ? "true" : "false");
5636 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
5639 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5642 /* media modifications */
5643 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
5646 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
5647 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
5648 GstWebRTCRTPTransceiverDirection new_dir;
5650 const GstSDPMedia *media;
5652 if (!pad->received_caps) {
5653 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
5658 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
5659 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
5664 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
5665 /* skip rejected media */
5666 if (gst_sdp_media_get_port (media) == 0) {
5667 /* FIXME: arrange for an appropriate flow return */
5668 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
5669 "a more correct flow return.");
5675 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
5680 new_dir = pad->trans->direction;
5681 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
5682 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5683 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
5684 "data at the moment. Not connecting input stream yet", pad->trans);
5689 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
5690 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
5691 pad->trans, pad->received_caps);
5692 _connect_input_stream (webrtc, pad);
5693 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
5697 gst_object_unref (old->data);
5698 webrtc->priv->pending_sink_transceivers =
5699 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
5703 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
5704 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
5706 TransportStream *item;
5709 _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
5710 _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
5712 if (sd->source == SDP_REMOTE) {
5715 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
5716 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
5718 if (g_strcmp0 (attr->key, "ssrc") == 0) {
5719 GStrv split = g_strsplit (attr->value, " ", 0);
5722 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
5723 && g_str_has_prefix (split[1], "cname:")) {
5724 g_ptr_array_add (item->remote_ssrcmap, ssrcmap_item_new (ssrc, i));
5731 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
5732 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5734 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
5735 item->stream, ufrag, pwd);
5738 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
5739 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5741 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
5742 item->stream, ufrag, pwd);
5748 if (sd->source == SDP_LOCAL) {
5749 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
5750 IceStreamItem *item =
5751 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
5753 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
5757 /* Add any pending trickle ICE candidates if we have both offer and answer */
5758 if (webrtc->current_local_description && webrtc->current_remote_description) {
5761 GstWebRTCSessionDescription *remote_sdp =
5762 webrtc->current_remote_description;
5764 /* Add any remote ICE candidates from the remote description to
5765 * support non-trickle peers first */
5766 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
5767 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
5768 _add_ice_candidates_from_sdp (webrtc, i, media);
5772 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
5773 IceCandidateItem *item =
5774 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
5775 IceCandidateItem, i);
5777 _add_ice_candidate (webrtc, item, TRUE);
5779 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
5780 ICE_UNLOCK (webrtc);
5784 * If connection's signaling state changed above, fire an event named
5785 * signalingstatechange at connection.
5787 if (signalling_state_changed) {
5788 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5789 webrtc->signaling_state);
5790 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5791 new_signaling_state);
5792 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
5795 g_object_notify (G_OBJECT (webrtc), "signaling-state");
5802 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5803 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
5805 /* If connection's signaling state is now stable, update the
5806 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
5807 * was true both before and after this update, queue a task to check
5808 * connection's [[needNegotiation]] slot and, if still true, fire a
5809 * simple event named negotiationneeded at connection.*/
5810 _update_need_negotiation (webrtc);
5811 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
5812 _check_need_negotiation_task (webrtc, NULL);
5817 g_strfreev (bundled);
5820 GstStructure *s = gst_structure_new ("application/x-gst-promise",
5821 "error", G_TYPE_ERROR, error, NULL);
5822 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
5823 g_clear_error (&error);
5831 _free_set_description_data (struct set_description *sd)
5834 gst_webrtc_session_description_free (sd->sdp);
5839 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
5840 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
5842 struct set_description *sd;
5844 if (remote_sdp == NULL)
5846 if (remote_sdp->sdp == NULL)
5849 sd = g_new0 (struct set_description, 1);
5850 sd->source = SDP_REMOTE;
5851 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
5853 if (!gst_webrtc_bin_enqueue_task (webrtc,
5854 (GstWebRTCBinFunc) _set_description_task, sd,
5855 (GDestroyNotify) _free_set_description_data, promise)) {
5857 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5858 "Could not set remote description. webrtcbin is closed.");
5859 GstStructure *s = gst_structure_new ("application/x-gst-promise",
5860 "error", G_TYPE_ERROR, error, NULL);
5862 gst_promise_reply (promise, s);
5864 g_clear_error (&error);
5871 gst_promise_reply (promise, NULL);
5872 g_return_if_reached ();
5877 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
5878 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
5880 struct set_description *sd;
5882 if (local_sdp == NULL)
5884 if (local_sdp->sdp == NULL)
5887 sd = g_new0 (struct set_description, 1);
5888 sd->source = SDP_LOCAL;
5889 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
5891 if (!gst_webrtc_bin_enqueue_task (webrtc,
5892 (GstWebRTCBinFunc) _set_description_task, sd,
5893 (GDestroyNotify) _free_set_description_data, promise)) {
5895 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5896 "Could not set remote description. webrtcbin is closed");
5897 GstStructure *s = gst_structure_new ("application/x-gst-promise",
5898 "error", G_TYPE_ERROR, error, NULL);
5900 gst_promise_reply (promise, s);
5902 g_clear_error (&error);
5909 gst_promise_reply (promise, NULL);
5910 g_return_if_reached ();
5914 static GstStructure *
5915 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
5917 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
5918 IceCandidateItem new;
5919 new.mlineindex = item->mlineindex;
5920 new.candidate = g_steal_pointer (&item->candidate);
5923 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5924 ICE_UNLOCK (webrtc);
5926 _add_ice_candidate (webrtc, item, FALSE);
5933 _free_ice_candidate_item (IceCandidateItem * item)
5935 _clear_ice_candidate_item (item);
5940 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
5943 IceCandidateItem *item;
5945 item = g_new0 (IceCandidateItem, 1);
5946 item->mlineindex = mline;
5947 if (attr && attr[0] != 0) {
5948 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
5949 item->candidate = g_strdup (attr);
5950 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
5951 item->candidate = g_strdup_printf ("a=%s", attr);
5953 gst_webrtc_bin_enqueue_task (webrtc,
5954 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
5955 (GDestroyNotify) _free_ice_candidate_item, NULL);
5958 static GstStructure *
5959 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
5965 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
5966 ICE_UNLOCK (webrtc);
5967 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
5968 return NULL; /* Nothing to process */
5970 /* Take the array so we can process it all and free it later
5971 * without holding the lock
5972 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
5974 items = webrtc->priv->pending_local_ice_candidates;
5975 /* Replace with a new array */
5976 webrtc->priv->pending_local_ice_candidates =
5977 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
5978 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
5979 (GDestroyNotify) _clear_ice_candidate_item);
5980 ICE_UNLOCK (webrtc);
5982 for (i = 0; i < items->len; i++) {
5983 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
5984 const gchar *cand = item->candidate;
5986 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
5987 /* stripping away "a=" */
5991 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
5992 item->mlineindex, cand);
5994 /* First, merge this ice candidate into the appropriate mline
5995 * in the local-description SDP.
5996 * Second, emit the on-ice-candidate signal for the app.
5998 * FIXME: This ICE candidate should be stored somewhere with
5999 * the associated mid and also merged back into any subsequent
6000 * local descriptions on renegotiation */
6001 if (webrtc->current_local_description)
6002 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
6003 item->mlineindex, cand);
6004 if (webrtc->pending_local_description)
6005 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
6006 item->mlineindex, cand);
6009 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
6010 0, item->mlineindex, cand);
6014 g_array_free (items, TRUE);
6020 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
6021 gchar * candidate, GstWebRTCBin * webrtc)
6023 IceCandidateItem item;
6024 gboolean queue_task = FALSE;
6026 item.mlineindex = session_id;
6027 item.candidate = g_strdup (candidate);
6030 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
6032 /* Let the first pending candidate queue a task each time, which will
6033 * handle any that arrive between now and when the task runs */
6034 if (webrtc->priv->pending_local_ice_candidates->len == 1)
6036 ICE_UNLOCK (webrtc);
6039 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
6040 gst_webrtc_bin_enqueue_task (webrtc,
6041 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
6048 GstPromise *promise;
6052 _free_get_stats (struct get_stats *stats)
6055 gst_object_unref (stats->pad);
6057 gst_promise_unref (stats->promise);
6061 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
6062 static GstStructure *
6063 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
6065 /* Our selector is the pad,
6066 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
6069 return gst_webrtc_bin_create_stats (webrtc, stats->pad);
6073 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
6074 GstPromise * promise)
6076 struct get_stats *stats;
6078 g_return_if_fail (promise != NULL);
6079 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
6081 stats = g_new0 (struct get_stats, 1);
6082 stats->promise = gst_promise_ref (promise);
6083 /* FIXME: check that pad exists in element */
6085 stats->pad = gst_object_ref (pad);
6087 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
6088 stats, (GDestroyNotify) _free_get_stats, promise)) {
6090 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
6091 "Could not retrieve statistics. webrtcbin is closed.");
6092 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6093 "error", G_TYPE_ERROR, error, NULL);
6095 gst_promise_reply (promise, s);
6097 g_clear_error (&error);
6101 static GstWebRTCRTPTransceiver *
6102 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
6103 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
6105 WebRTCTransceiver *trans;
6107 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
6113 _create_webrtc_transceiver (webrtc, direction, -1,
6114 webrtc_kind_from_caps (caps), caps);
6115 GST_LOG_OBJECT (webrtc,
6116 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
6120 return gst_object_ref (trans);
6124 _deref_and_unref (GstObject ** object)
6126 gst_clear_object (object);
6130 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
6132 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
6137 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
6139 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6140 GstWebRTCRTPTransceiver *trans =
6141 g_ptr_array_index (webrtc->priv->transceivers, i);
6142 gst_object_ref (trans);
6143 g_array_append_val (arr, trans);
6150 static GstWebRTCRTPTransceiver *
6151 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
6153 GstWebRTCRTPTransceiver *trans = NULL;
6157 if (idx >= webrtc->priv->transceivers->len) {
6158 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
6162 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
6163 gst_object_ref (trans);
6171 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
6175 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
6176 g_return_val_if_fail (uri != NULL, FALSE);
6178 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
6181 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
6188 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
6190 GstPad *gpad = GST_PAD_CAST (user_data);
6192 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
6193 gst_pad_store_sticky_event (gpad, *event);
6198 static WebRTCDataChannel *
6199 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
6200 GstStructure * init_params)
6203 gint max_packet_lifetime;
6204 gint max_retransmits;
6205 const gchar *protocol;
6206 gboolean negotiated;
6208 GstWebRTCPriorityType priority;
6209 WebRTCDataChannel *ret;
6210 gint max_channels = 65534;
6212 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
6213 g_return_val_if_fail (label != NULL, NULL);
6214 g_return_val_if_fail (strlen (label) <= 65535, NULL);
6215 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
6218 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
6221 || !gst_structure_get_int (init_params, "max-packet-lifetime",
6222 &max_packet_lifetime))
6223 max_packet_lifetime = -1;
6225 || !gst_structure_get_int (init_params, "max-retransmits",
6227 max_retransmits = -1;
6228 /* both retransmits and lifetime cannot be set */
6229 g_return_val_if_fail ((max_packet_lifetime == -1)
6230 || (max_retransmits == -1), NULL);
6233 || !(protocol = gst_structure_get_string (init_params, "protocol")))
6235 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
6238 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
6240 if (!negotiated || !init_params
6241 || !gst_structure_get_int (init_params, "id", &id))
6244 g_return_val_if_fail (id != -1, NULL);
6245 g_return_val_if_fail (id < 65535, NULL);
6248 || !gst_structure_get_enum (init_params, "priority",
6249 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
6250 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
6252 /* FIXME: clamp max-retransmits and max-packet-lifetime */
6254 if (webrtc->priv->sctp_transport) {
6255 /* Let transport be the connection's [[SctpTransport]] slot.
6257 * If the [[DataChannelId]] slot is not null, transport is in
6258 * connected state and [[DataChannelId]] is greater or equal to the
6259 * transport's [[MaxChannels]] slot, throw an OperationError.
6261 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
6264 g_return_val_if_fail (id <= max_channels, NULL);
6267 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
6268 !_have_sctp_elements (webrtc))
6273 /* check if the id has been used already */
6275 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
6277 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
6278 ("Attempting to add a data channel with a duplicate ID: %i", id),
6284 } else if (webrtc->current_local_description
6285 && webrtc->current_remote_description && webrtc->priv->sctp_transport
6286 && webrtc->priv->sctp_transport->transport) {
6287 /* else we can only generate an id if we're configured already. The other
6288 * case for generating an id is on sdp setting */
6289 id = _generate_data_channel_id (webrtc);
6291 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
6292 ("%s", "Failed to generate an identifier for a data channel"), NULL);
6299 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
6300 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
6301 "max-retransmits", max_retransmits, "protocol", protocol,
6302 "negotiated", negotiated, "id", id, "priority", priority, NULL);
6310 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
6311 gst_bin_add (GST_BIN (webrtc), ret->appsink);
6313 gst_element_sync_state_with_parent (ret->appsrc);
6314 gst_element_sync_state_with_parent (ret->appsink);
6316 ret = gst_object_ref (ret);
6317 ret->webrtcbin = webrtc;
6318 g_ptr_array_add (webrtc->priv->data_channels, ret);
6321 gst_webrtc_bin_update_sctp_priority (webrtc);
6322 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
6323 if (webrtc->priv->sctp_transport &&
6324 webrtc->priv->sctp_transport->association_established
6325 && !ret->parent.negotiated) {
6326 webrtc_data_channel_start_negotiation (ret);
6328 _update_need_negotiation (webrtc);
6335 /* === rtpbin signal implementations === */
6338 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
6339 GstWebRTCBin * webrtc)
6341 gchar *new_pad_name = NULL;
6343 new_pad_name = gst_pad_get_name (new_pad);
6344 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
6345 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
6346 guint32 session_id = 0, ssrc = 0, pt = 0;
6347 GstWebRTCRTPTransceiver *rtp_trans;
6348 WebRTCTransceiver *trans;
6349 TransportStream *stream;
6350 GstWebRTCBinPad *pad;
6351 guint media_idx = 0;
6352 gboolean found_ssrc = FALSE;
6355 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
6357 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
6361 stream = _find_transport_for_session (webrtc, session_id);
6363 g_warn_if_reached ();
6365 media_idx = session_id;
6367 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
6368 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
6369 if (item->ssrc == ssrc) {
6370 media_idx = item->media_idx;
6377 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
6380 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
6382 g_warn_if_reached ();
6383 trans = WEBRTC_TRANSCEIVER (rtp_trans);
6384 g_assert (trans->stream == stream);
6386 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
6388 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
6389 " for rtpbin pad name %s", pad, new_pad_name);
6391 g_warn_if_reached ();
6392 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
6394 if (webrtc->priv->running)
6395 gst_pad_set_active (GST_PAD (pad), TRUE);
6396 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
6397 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
6398 _remove_pending_pad (webrtc, pad);
6400 gst_object_unref (pad);
6402 g_free (new_pad_name);
6405 /* only used for the receiving streams */
6407 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
6408 GstWebRTCBin * webrtc)
6410 TransportStream *stream;
6413 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
6416 stream = _find_transport_for_session (webrtc, session_id);
6418 goto unknown_session;
6420 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
6423 GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
6424 "session %d", ret, pt, session_id);
6430 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
6436 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
6438 GstStructure *s = user_data;
6440 gst_structure_id_set_value (s, field_id, value);
6446 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
6447 GstWebRTCBin * webrtc)
6449 TransportStream *stream;
6450 gboolean have_rtx = FALSE;
6451 GstStructure *pt_map = NULL;
6452 GstElement *ret = NULL;
6454 stream = _find_transport_for_session (webrtc, session_id);
6457 have_rtx = transport_stream_get_pt (stream, "RTX", -1) != 0;
6459 GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
6460 " with pt map %" GST_PTR_FORMAT, stream, pt_map);
6466 GstStructure *merged_local_rtx_ssrc_map =
6467 gst_structure_new_empty ("application/x-rtp-ssrc-map");
6470 if (stream->rtxsend) {
6471 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
6475 GST_INFO ("creating AUX sender");
6476 ret = gst_bin_new (NULL);
6477 rtx = gst_element_factory_make ("rtprtxsend", NULL);
6478 g_object_set (rtx, "max-size-packets", 500, NULL);
6479 _set_rtx_ptmap_from_stream (webrtc, stream);
6481 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6482 WebRTCTransceiver *trans =
6483 WEBRTC_TRANSCEIVER (g_ptr_array_index (webrtc->priv->transceivers,
6486 if (trans->stream == stream && trans->local_rtx_ssrc_map)
6487 gst_structure_foreach (trans->local_rtx_ssrc_map,
6488 _merge_structure, merged_local_rtx_ssrc_map);
6491 g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
6492 gst_structure_free (merged_local_rtx_ssrc_map);
6494 gst_bin_add (GST_BIN (ret), rtx);
6496 pad = gst_element_get_static_pad (rtx, "src");
6497 name = g_strdup_printf ("src_%u", session_id);
6498 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
6500 gst_object_unref (pad);
6502 pad = gst_element_get_static_pad (rtx, "sink");
6503 name = g_strdup_printf ("sink_%u", session_id);
6504 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
6506 gst_object_unref (pad);
6508 stream->rtxsend = gst_object_ref (rtx);
6513 gst_structure_free (pt_map);
6519 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
6520 GstWebRTCBin * webrtc)
6522 GstElement *ret = NULL;
6523 GstElement *prev = NULL;
6524 GstPad *sinkpad = NULL;
6525 TransportStream *stream;
6527 GValue red_pt_array = { 0, };
6528 gboolean have_red_pt = FALSE;
6530 g_value_init (&red_pt_array, GST_TYPE_ARRAY);
6532 stream = _find_transport_for_session (webrtc, session_id);
6537 for (i = 0; i < stream->ptmap->len; i++) {
6538 PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
6540 if (!gst_caps_is_empty (item->caps)) {
6541 GstStructure *s = gst_caps_get_structure (item->caps, 0);
6543 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "RED")) {
6544 GValue ptval = { 0, };
6546 g_value_init (&ptval, G_TYPE_INT);
6547 g_value_set_int (&ptval, item->pt);
6548 gst_value_array_append_value (&red_pt_array, &ptval);
6549 g_value_unset (&ptval);
6556 rtx_pt = transport_stream_get_pt (stream, "RTX", -1);
6559 GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
6562 if (have_red_pt || rtx_pt)
6563 ret = gst_bin_new (NULL);
6566 if (stream->rtxreceive) {
6567 GST_WARNING_OBJECT (webrtc,
6568 "rtprtxreceive already created! rtpbin bug?!");
6572 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
6573 _set_rtx_ptmap_from_stream (webrtc, stream);
6575 gst_bin_add (GST_BIN (ret), stream->rtxreceive);
6577 sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
6579 prev = gst_object_ref (stream->rtxreceive);
6583 GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
6585 GST_DEBUG_OBJECT (webrtc, "Creating RED decoder in session %u", session_id);
6587 gst_bin_add (GST_BIN (ret), rtpreddec);
6589 g_object_set_property (G_OBJECT (rtpreddec), "payloads", &red_pt_array);
6592 gst_element_link (prev, rtpreddec);
6594 sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
6600 gchar *name = g_strdup_printf ("sink_%u", session_id);
6601 GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
6603 gst_object_unref (sinkpad);
6604 gst_element_add_pad (ret, ghost);
6608 gchar *name = g_strdup_printf ("src_%u", session_id);
6609 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
6610 GstPad *ghost = gst_ghost_pad_new (name, srcpad);
6612 gst_object_unref (srcpad);
6613 gst_element_add_pad (ret, ghost);
6617 g_value_unset (&red_pt_array);
6622 gst_object_unref (ret);
6627 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
6628 guint ssrc, guint pt, GstWebRTCBin * webrtc)
6630 TransportStream *stream;
6631 GstElement *ret = NULL;
6633 GObject *internal_storage;
6635 stream = _find_transport_for_session (webrtc, session_id);
6637 /* TODO: for now, we only support ulpfec, but once we support
6638 * more algorithms, if the remote may use more than one algorithm,
6639 * we will want to do the following:
6641 * + Return a bin here, with the relevant FEC decoders plugged in
6642 * and their payload type set to 0
6643 * + Enable the decoders by setting the payload type only when
6644 * we detect it (by connecting to ptdemux:new-payload-type for
6650 for (i = 0; i < stream->ptmap->len; i++) {
6651 PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
6653 if (item->pt == pt) {
6654 fec_pt = transport_stream_get_pt (stream, "ULPFEC", item->media_idx);
6661 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
6662 fec_pt, session_id);
6663 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
6664 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
6667 g_object_set (ret, "pt", fec_pt, "storage", internal_storage, NULL);
6668 g_object_unref (internal_storage);
6675 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6676 GstWebRTCBin * webrtc)
6678 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
6682 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6683 GstWebRTCBin * webrtc)
6685 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
6689 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6690 GstWebRTCBin * webrtc)
6692 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
6697 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6698 GstWebRTCBin * webrtc)
6700 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
6704 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6705 GstWebRTCBin * webrtc)
6707 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
6711 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
6712 GstWebRTCBin * webrtc)
6714 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
6718 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
6719 GstWebRTCBin * webrtc)
6721 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
6725 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
6726 GstWebRTCBin * webrtc)
6728 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
6732 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6733 GstWebRTCBin * webrtc)
6735 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
6739 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6740 GstWebRTCBin * webrtc)
6742 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
6747 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6748 GstWebRTCBin * webrtc)
6750 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
6755 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
6756 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
6758 TransportStream *stream;
6762 GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
6763 "session %u ssrc %u", jitterbuffer, session_id, ssrc);
6765 if (!(stream = _find_transport_for_session (webrtc, session_id))) {
6766 g_warn_if_reached ();
6770 /* XXX: this will fail with no ssrc in the remote sdp as used with e.g. simulcast
6771 * newer SDP versions from chrome/firefox */
6772 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
6773 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
6775 if (item->ssrc == ssrc) {
6776 GstWebRTCRTPTransceiver *trans;
6779 trans = _find_transceiver_for_mline (webrtc, item->media_idx);
6781 g_warn_if_reached ();
6785 do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
6786 /* We don't set do-retransmission on rtpbin as we want per-session control */
6787 GST_LOG_OBJECT (webrtc, "setting do-nack=%s for transceiver %"
6788 GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
6789 " rtp session %u ssrc %u", do_nack ? "true" : "false", trans, stream,
6791 g_object_set (jitterbuffer, "do-retransmission", do_nack, NULL);
6793 g_weak_ref_set (&item->rtpjitterbuffer, jitterbuffer);
6802 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
6803 guint session_id, GstWebRTCBin * webrtc)
6805 guint64 latency = webrtc->priv->jb_latency;
6807 /* Add an extra 50 ms for safey */
6808 latency += RTPSTORAGE_EXTRA_TIME;
6809 latency *= GST_MSECOND;
6811 g_object_set (storage, "size-time", latency, NULL);
6815 _create_rtpbin (GstWebRTCBin * webrtc)
6819 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
6822 /* mandated by WebRTC */
6823 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
6825 g_object_set (rtpbin, "do-lost", TRUE, NULL);
6827 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
6829 g_signal_connect (rtpbin, "request-pt-map",
6830 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
6831 g_signal_connect (rtpbin, "request-aux-sender",
6832 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
6833 g_signal_connect (rtpbin, "request-aux-receiver",
6834 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
6835 g_signal_connect (rtpbin, "new-storage",
6836 G_CALLBACK (on_rtpbin_new_storage), webrtc);
6837 g_signal_connect (rtpbin, "request-fec-decoder-full",
6838 G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
6839 g_signal_connect (rtpbin, "on-bye-ssrc",
6840 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
6841 g_signal_connect (rtpbin, "on-bye-timeout",
6842 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
6843 g_signal_connect (rtpbin, "on-new-ssrc",
6844 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
6845 g_signal_connect (rtpbin, "on-new-sender-ssrc",
6846 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
6847 g_signal_connect (rtpbin, "on-sender-ssrc-active",
6848 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
6849 g_signal_connect (rtpbin, "on-sender-timeout",
6850 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
6851 g_signal_connect (rtpbin, "on-ssrc-active",
6852 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
6853 g_signal_connect (rtpbin, "on-ssrc-collision",
6854 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
6855 g_signal_connect (rtpbin, "on-ssrc-sdes",
6856 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
6857 g_signal_connect (rtpbin, "on-ssrc-validated",
6858 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
6859 g_signal_connect (rtpbin, "on-timeout",
6860 G_CALLBACK (on_rtpbin_timeout), webrtc);
6861 g_signal_connect (rtpbin, "new-jitterbuffer",
6862 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
6867 static GstStateChangeReturn
6868 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
6870 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6871 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
6873 GST_DEBUG ("changing state: %s => %s",
6874 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
6875 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
6877 switch (transition) {
6878 case GST_STATE_CHANGE_NULL_TO_READY:{
6879 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6880 return GST_STATE_CHANGE_FAILURE;
6881 _start_thread (webrtc);
6883 _update_need_negotiation (webrtc);
6887 case GST_STATE_CHANGE_READY_TO_PAUSED:
6888 webrtc->priv->running = TRUE;
6894 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6895 if (ret == GST_STATE_CHANGE_FAILURE)
6898 switch (transition) {
6899 case GST_STATE_CHANGE_READY_TO_PAUSED:
6900 /* Mangle the return value to NO_PREROLL as that's what really is
6901 * occurring here however cannot be propagated correctly due to nicesrc
6902 * requiring that it be in PLAYING already in order to send/receive
6904 ret = GST_STATE_CHANGE_NO_PREROLL;
6906 case GST_STATE_CHANGE_PAUSED_TO_READY:
6907 webrtc->priv->running = FALSE;
6909 case GST_STATE_CHANGE_READY_TO_NULL:
6910 _stop_thread (webrtc);
6919 static GstPadProbeReturn
6920 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
6922 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
6924 return GST_PAD_PROBE_OK;
6929 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
6930 const gchar * name, const GstCaps * caps)
6932 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6933 GstWebRTCRTPTransceiver *trans = NULL;
6934 GstWebRTCBinPad *pad = NULL;
6936 gboolean lock_mline = FALSE;
6938 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6941 if (templ->direction != GST_PAD_SINK ||
6942 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
6943 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
6949 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
6950 /* no name given when requesting the pad, use next available int */
6951 serial = webrtc->priv->max_sink_pad_serial++;
6953 /* parse serial number from requested padname */
6954 serial = g_ascii_strtoull (&name[5], NULL, 10);
6959 GstWebRTCBinPad *pad2;
6961 trans = _find_transceiver_for_mline (webrtc, serial);
6964 /* Reject transceivers that are only for receiving ... */
6965 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
6966 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
6968 g_enum_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
6970 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6971 " existing m-line %d, but the transceiver's direction is %s",
6972 name, serial, direction);
6977 /* Reject transceivers that already have a pad allocated */
6978 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
6980 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
6981 " but the transceiver associated with this m-line already has pad"
6982 " %s", name, serial, GST_PAD_NAME (pad2));
6983 gst_object_unref (pad2);
6988 GST_OBJECT_LOCK (trans);
6989 if (trans->codec_preferences &&
6990 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
6991 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6992 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
6993 " don't match existing codec preferences %" GST_PTR_FORMAT,
6994 name, serial, caps, trans->codec_preferences);
6995 GST_OBJECT_UNLOCK (trans);
6998 GST_OBJECT_UNLOCK (trans);
7000 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
7001 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
7003 if (trans->kind != kind) {
7004 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7005 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
7006 " don't match transceiver kind %d",
7007 name, serial, caps, trans->kind);
7015 /* Let's try to find a free transceiver that matches */
7017 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
7020 kind = webrtc_kind_from_caps (caps);
7022 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
7023 GstWebRTCRTPTransceiver *tmptrans =
7024 g_ptr_array_index (webrtc->priv->transceivers, i);
7025 GstWebRTCBinPad *pad2;
7026 gboolean has_matching_caps;
7028 /* Ignore transceivers with a non-matching kind */
7029 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
7030 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
7033 /* Ignore stopped transmitters */
7034 if (tmptrans->stopped)
7037 /* Ignore transceivers that are only for receiving ... */
7038 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
7039 || tmptrans->direction ==
7040 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
7043 /* Ignore transceivers that already have a pad allocated */
7044 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
7046 gst_object_unref (pad2);
7050 GST_OBJECT_LOCK (tmptrans);
7051 has_matching_caps = (caps && tmptrans->codec_preferences &&
7052 !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
7053 GST_OBJECT_UNLOCK (tmptrans);
7054 /* Ignore transceivers with non-matching caps */
7055 if (!has_matching_caps)
7064 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
7065 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
7066 webrtc_kind_from_caps (caps), NULL));
7067 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
7069 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
7070 " for mline %u", trans, serial);
7072 if (!_update_transceiver_kind_from_caps (trans, caps))
7073 GST_WARNING_OBJECT (webrtc,
7074 "Trying to change transceiver %d kind from %d to %d",
7075 serial, trans->kind, webrtc_kind_from_caps (caps));
7078 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
7080 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
7081 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
7082 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
7083 webrtc->priv->pending_sink_transceivers =
7084 g_list_append (webrtc->priv->pending_sink_transceivers,
7085 gst_object_ref (pad));
7088 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
7089 wtrans->mline_locked = TRUE;
7090 trans->mline = serial;
7095 _add_pad (webrtc, pad);
7097 return GST_PAD (pad);
7105 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
7107 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7108 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7110 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
7112 /* remove the transceiver from the pad so that subsequent code doesn't use
7113 * a possibly dead transceiver */
7115 if (webrtc_pad->trans)
7116 gst_object_unref (webrtc_pad->trans);
7117 webrtc_pad->trans = NULL;
7118 gst_caps_replace (&webrtc_pad->received_caps, NULL);
7121 _remove_pad (webrtc, webrtc_pad);
7124 _update_need_negotiation (webrtc);
7129 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
7134 /* Add an extra 50 ms for safety */
7135 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
7136 latency_ns *= GST_MSECOND;
7138 for (i = 0; i < webrtc->priv->transports->len; i++) {
7139 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
7140 GObject *storage = NULL;
7142 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
7145 g_object_set (storage, "size-time", latency_ns, NULL);
7147 g_object_unref (storage);
7152 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
7153 const GValue * value, GParamSpec * pspec)
7155 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7158 case PROP_STUN_SERVER:
7159 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
7160 g_value_get_string (value));
7162 case PROP_TURN_SERVER:
7163 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
7164 g_value_get_string (value));
7166 case PROP_BUNDLE_POLICY:
7167 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
7168 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
7170 webrtc->bundle_policy = g_value_get_enum (value);
7173 case PROP_ICE_TRANSPORT_POLICY:
7174 webrtc->ice_transport_policy = g_value_get_enum (value);
7175 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
7176 webrtc->ice_transport_policy ==
7177 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
7180 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
7181 webrtc->priv->jb_latency = g_value_get_uint (value);
7182 _update_rtpstorage_latency (webrtc);
7185 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7191 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
7192 GValue * value, GParamSpec * pspec)
7194 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7198 case PROP_CONNECTION_STATE:
7199 g_value_set_enum (value, webrtc->peer_connection_state);
7201 case PROP_SIGNALING_STATE:
7202 g_value_set_enum (value, webrtc->signaling_state);
7204 case PROP_ICE_GATHERING_STATE:
7205 g_value_set_enum (value, webrtc->ice_gathering_state);
7207 case PROP_ICE_CONNECTION_STATE:
7208 g_value_set_enum (value, webrtc->ice_connection_state);
7210 case PROP_LOCAL_DESCRIPTION:
7211 if (webrtc->pending_local_description)
7212 g_value_set_boxed (value, webrtc->pending_local_description);
7213 else if (webrtc->current_local_description)
7214 g_value_set_boxed (value, webrtc->current_local_description);
7216 g_value_set_boxed (value, NULL);
7218 case PROP_CURRENT_LOCAL_DESCRIPTION:
7219 g_value_set_boxed (value, webrtc->current_local_description);
7221 case PROP_PENDING_LOCAL_DESCRIPTION:
7222 g_value_set_boxed (value, webrtc->pending_local_description);
7224 case PROP_REMOTE_DESCRIPTION:
7225 if (webrtc->pending_remote_description)
7226 g_value_set_boxed (value, webrtc->pending_remote_description);
7227 else if (webrtc->current_remote_description)
7228 g_value_set_boxed (value, webrtc->current_remote_description);
7230 g_value_set_boxed (value, NULL);
7232 case PROP_CURRENT_REMOTE_DESCRIPTION:
7233 g_value_set_boxed (value, webrtc->current_remote_description);
7235 case PROP_PENDING_REMOTE_DESCRIPTION:
7236 g_value_set_boxed (value, webrtc->pending_remote_description);
7238 case PROP_STUN_SERVER:
7239 g_value_take_string (value,
7240 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
7242 case PROP_TURN_SERVER:
7243 g_value_take_string (value,
7244 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
7246 case PROP_BUNDLE_POLICY:
7247 g_value_set_enum (value, webrtc->bundle_policy);
7249 case PROP_ICE_TRANSPORT_POLICY:
7250 g_value_set_enum (value, webrtc->ice_transport_policy);
7252 case PROP_ICE_AGENT:
7253 g_value_set_object (value, webrtc->priv->ice);
7256 g_value_set_uint (value, webrtc->priv->jb_latency);
7258 case PROP_SCTP_TRANSPORT:
7259 g_value_set_object (value, webrtc->priv->sctp_transport);
7262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7269 gst_webrtc_bin_constructed (GObject * object)
7271 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7274 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
7275 webrtc->priv->ice = gst_webrtc_ice_new (name);
7277 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
7278 (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
7284 _free_pending_pad (GstPad * pad)
7286 gst_object_unref (pad);
7290 gst_webrtc_bin_dispose (GObject * object)
7292 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7294 if (webrtc->priv->ice)
7295 gst_object_unref (webrtc->priv->ice);
7296 webrtc->priv->ice = NULL;
7298 if (webrtc->priv->ice_stream_map)
7299 g_array_free (webrtc->priv->ice_stream_map, TRUE);
7300 webrtc->priv->ice_stream_map = NULL;
7302 g_clear_object (&webrtc->priv->sctp_transport);
7304 G_OBJECT_CLASS (parent_class)->dispose (object);
7308 gst_webrtc_bin_finalize (GObject * object)
7310 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7312 if (webrtc->priv->transports)
7313 g_ptr_array_free (webrtc->priv->transports, TRUE);
7314 webrtc->priv->transports = NULL;
7316 if (webrtc->priv->transceivers)
7317 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
7318 webrtc->priv->transceivers = NULL;
7320 if (webrtc->priv->data_channels)
7321 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
7322 webrtc->priv->data_channels = NULL;
7324 if (webrtc->priv->pending_data_channels)
7325 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
7326 webrtc->priv->pending_data_channels = NULL;
7328 if (webrtc->priv->pending_remote_ice_candidates)
7329 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
7330 webrtc->priv->pending_remote_ice_candidates = NULL;
7332 if (webrtc->priv->pending_local_ice_candidates)
7333 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
7334 webrtc->priv->pending_local_ice_candidates = NULL;
7336 if (webrtc->priv->pending_pads)
7337 g_list_free_full (webrtc->priv->pending_pads,
7338 (GDestroyNotify) _free_pending_pad);
7339 webrtc->priv->pending_pads = NULL;
7341 if (webrtc->priv->pending_sink_transceivers)
7342 g_list_free_full (webrtc->priv->pending_sink_transceivers,
7343 (GDestroyNotify) gst_object_unref);
7344 webrtc->priv->pending_sink_transceivers = NULL;
7346 if (webrtc->current_local_description)
7347 gst_webrtc_session_description_free (webrtc->current_local_description);
7348 webrtc->current_local_description = NULL;
7349 if (webrtc->pending_local_description)
7350 gst_webrtc_session_description_free (webrtc->pending_local_description);
7351 webrtc->pending_local_description = NULL;
7353 if (webrtc->current_remote_description)
7354 gst_webrtc_session_description_free (webrtc->current_remote_description);
7355 webrtc->current_remote_description = NULL;
7356 if (webrtc->pending_remote_description)
7357 gst_webrtc_session_description_free (webrtc->pending_remote_description);
7358 webrtc->pending_remote_description = NULL;
7360 if (webrtc->priv->last_generated_answer)
7361 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
7362 webrtc->priv->last_generated_answer = NULL;
7363 if (webrtc->priv->last_generated_offer)
7364 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
7365 webrtc->priv->last_generated_offer = NULL;
7367 g_mutex_clear (DC_GET_LOCK (webrtc));
7368 g_mutex_clear (ICE_GET_LOCK (webrtc));
7369 g_mutex_clear (PC_GET_LOCK (webrtc));
7370 g_cond_clear (PC_GET_COND (webrtc));
7372 G_OBJECT_CLASS (parent_class)->finalize (object);
7376 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
7378 GObjectClass *gobject_class = (GObjectClass *) klass;
7379 GstElementClass *element_class = (GstElementClass *) klass;
7381 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
7382 element_class->release_pad = gst_webrtc_bin_release_pad;
7383 element_class->change_state = gst_webrtc_bin_change_state;
7385 gst_element_class_add_static_pad_template_with_gtype (element_class,
7386 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
7387 gst_element_class_add_static_pad_template (element_class, &src_template);
7389 gst_element_class_set_metadata (element_class, "WebRTC Bin",
7390 "Filter/Network/WebRTC", "A bin for webrtc connections",
7391 "Matthew Waters <matthew@centricular.com>");
7393 gobject_class->constructed = gst_webrtc_bin_constructed;
7394 gobject_class->get_property = gst_webrtc_bin_get_property;
7395 gobject_class->set_property = gst_webrtc_bin_set_property;
7396 gobject_class->dispose = gst_webrtc_bin_dispose;
7397 gobject_class->finalize = gst_webrtc_bin_finalize;
7399 g_object_class_install_property (gobject_class,
7400 PROP_LOCAL_DESCRIPTION,
7401 g_param_spec_boxed ("local-description", "Local Description",
7402 "The local SDP description in use for this connection. "
7403 "Favours a pending description over the current description",
7404 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7405 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7407 g_object_class_install_property (gobject_class,
7408 PROP_CURRENT_LOCAL_DESCRIPTION,
7409 g_param_spec_boxed ("current-local-description",
7410 "Current Local Description",
7411 "The local description that was successfully negotiated the last time "
7412 "the connection transitioned into the stable state",
7413 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7414 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7416 g_object_class_install_property (gobject_class,
7417 PROP_PENDING_LOCAL_DESCRIPTION,
7418 g_param_spec_boxed ("pending-local-description",
7419 "Pending Local Description",
7420 "The local description that is in the process of being negotiated plus "
7421 "any local candidates that have been generated by the ICE Agent since the "
7422 "offer or answer was created",
7423 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7424 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7426 g_object_class_install_property (gobject_class,
7427 PROP_REMOTE_DESCRIPTION,
7428 g_param_spec_boxed ("remote-description", "Remote Description",
7429 "The remote SDP description to use for this connection. "
7430 "Favours a pending description over the current description",
7431 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7432 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7434 g_object_class_install_property (gobject_class,
7435 PROP_CURRENT_REMOTE_DESCRIPTION,
7436 g_param_spec_boxed ("current-remote-description",
7437 "Current Remote Description",
7438 "The last remote description that was successfully negotiated the last "
7439 "time the connection transitioned into the stable state plus any remote "
7440 "candidates that have been supplied via addIceCandidate() since the offer "
7441 "or answer was created",
7442 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7443 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7445 g_object_class_install_property (gobject_class,
7446 PROP_PENDING_REMOTE_DESCRIPTION,
7447 g_param_spec_boxed ("pending-remote-description",
7448 "Pending Remote Description",
7449 "The remote description that is in the process of being negotiated, "
7450 "complete with any remote candidates that have been supplied via "
7451 "addIceCandidate() since the offer or answer was created",
7452 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7453 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7455 g_object_class_install_property (gobject_class,
7457 g_param_spec_string ("stun-server", "STUN Server",
7458 "The STUN server of the form stun://hostname:port",
7459 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7461 g_object_class_install_property (gobject_class,
7463 g_param_spec_string ("turn-server", "TURN Server",
7464 "The TURN server of the form turn(s)://username:password@host:port. "
7465 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
7466 "if you wish to use multiple TURN servers",
7467 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7469 g_object_class_install_property (gobject_class,
7470 PROP_CONNECTION_STATE,
7471 g_param_spec_enum ("connection-state", "Connection State",
7472 "The overall connection state of this element",
7473 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
7474 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
7475 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7477 g_object_class_install_property (gobject_class,
7478 PROP_SIGNALING_STATE,
7479 g_param_spec_enum ("signaling-state", "Signaling State",
7480 "The signaling state of this element",
7481 GST_TYPE_WEBRTC_SIGNALING_STATE,
7482 GST_WEBRTC_SIGNALING_STATE_STABLE,
7483 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7485 g_object_class_install_property (gobject_class,
7486 PROP_ICE_CONNECTION_STATE,
7487 g_param_spec_enum ("ice-connection-state", "ICE connection state",
7488 "The collective connection state of all ICETransport's",
7489 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
7490 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
7491 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7493 g_object_class_install_property (gobject_class,
7494 PROP_ICE_GATHERING_STATE,
7495 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
7496 "The collective gathering state of all ICETransport's",
7497 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
7498 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
7499 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7501 g_object_class_install_property (gobject_class,
7503 g_param_spec_enum ("bundle-policy", "Bundle Policy",
7504 "The policy to apply for bundling",
7505 GST_TYPE_WEBRTC_BUNDLE_POLICY,
7506 GST_WEBRTC_BUNDLE_POLICY_NONE,
7507 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7509 g_object_class_install_property (gobject_class,
7510 PROP_ICE_TRANSPORT_POLICY,
7511 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
7512 "The policy to apply for ICE transport",
7513 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
7514 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
7515 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7517 g_object_class_install_property (gobject_class,
7519 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
7520 "The WebRTC ICE agent",
7521 GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7524 * GstWebRTCBin:latency:
7526 * Default duration to buffer in the jitterbuffers (in ms)
7531 g_object_class_install_property (gobject_class,
7533 g_param_spec_uint ("latency", "Latency",
7534 "Default duration to buffer in the jitterbuffers (in ms)",
7535 0, G_MAXUINT, DEFAULT_JB_LATENCY,
7536 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7539 * GstWebRTCBin:sctp-transport:
7541 * The WebRTC SCTP Transport
7545 g_object_class_install_property (gobject_class,
7546 PROP_SCTP_TRANSPORT,
7547 g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
7548 "The WebRTC SCTP Transport",
7549 GST_TYPE_WEBRTC_SCTP_TRANSPORT,
7550 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7553 * GstWebRTCBin::create-offer:
7554 * @object: the #webrtcbin
7555 * @options: (nullable): create-offer options
7556 * @promise: a #GstPromise which will contain the offer
7558 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
7559 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
7560 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7561 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
7562 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7565 * GstWebRTCBin::create-answer:
7566 * @object: the #webrtcbin
7567 * @options: (nullable): create-answer options
7568 * @promise: a #GstPromise which will contain the answer
7570 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
7571 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
7572 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7573 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
7574 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7577 * GstWebRTCBin::set-local-description:
7578 * @object: the #GstWebRTCBin
7579 * @desc: a #GstWebRTCSessionDescription description
7580 * @promise: (nullable): a #GstPromise to be notified when it's set
7582 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
7583 g_signal_new_class_handler ("set-local-description",
7584 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7585 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
7586 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7589 * GstWebRTCBin::set-remote-description:
7590 * @object: the #GstWebRTCBin
7591 * @desc: a #GstWebRTCSessionDescription description
7592 * @promise: (nullable): a #GstPromise to be notified when it's set
7594 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
7595 g_signal_new_class_handler ("set-remote-description",
7596 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7597 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
7598 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7601 * GstWebRTCBin::add-ice-candidate:
7602 * @object: the #webrtcbin
7603 * @mline_index: the index of the media description in the SDP
7604 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
7607 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
7608 g_signal_new_class_handler ("add-ice-candidate",
7609 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7610 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
7611 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7614 * GstWebRTCBin::get-stats:
7615 * @object: the #webrtcbin
7616 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
7617 * @promise: a #GstPromise for the result
7619 * The @promise will contain the result of retrieving the session statistics.
7620 * The structure will be named 'application/x-webrtc-stats and contain the
7621 * following based on the webrtc-stats spec available from
7622 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
7623 * and is constantly changing these statistics may be changed to fit with
7626 * Each field key is a unique identifier for each RTCStats
7627 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
7628 * GstStructure) in the RTCStatsReport
7629 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
7630 * field in the RTCStats subclass is outlined below.
7632 * Each statistics structure contains the following values as defined by
7633 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
7635 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
7636 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
7637 * "id" G_TYPE_STRING unique identifier
7639 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
7641 * "payload-type" G_TYPE_UINT the rtp payload number in use
7642 * "clock-rate" G_TYPE_UINT the rtp clock-rate
7644 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
7646 * "ssrc" G_TYPE_STRING the rtp sequence src in use
7647 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
7648 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
7649 * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
7650 * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
7651 * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
7653 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
7655 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
7656 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
7657 * "packets-lost" G_TYPE_UINT number of packets lost
7658 * "jitter" G_TYPE_DOUBLE packet jitter measured in secondss
7660 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
7662 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
7664 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
7666 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
7667 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
7669 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
7671 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7672 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7674 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
7676 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
7678 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
7680 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
7683 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
7684 g_signal_new_class_handler ("get-stats",
7685 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7686 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
7687 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
7690 * GstWebRTCBin::on-negotiation-needed:
7691 * @object: the #webrtcbin
7693 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
7694 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
7695 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
7698 * GstWebRTCBin::on-ice-candidate:
7699 * @object: the #webrtcbin
7700 * @mline_index: the index of the media description in the SDP
7701 * @candidate: the ICE candidate
7703 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
7704 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
7705 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7706 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7709 * GstWebRTCBin::on-new-transceiver:
7710 * @object: the #webrtcbin
7711 * @candidate: the new #GstWebRTCRTPTransceiver
7713 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
7714 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
7715 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7716 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
7719 * GstWebRTCBin::on-data-channel:
7720 * @object: the #GstWebRTCBin
7721 * @candidate: the new `GstWebRTCDataChannel`
7723 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
7724 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
7725 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7726 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
7729 * GstWebRTCBin::add-transceiver:
7730 * @object: the #webrtcbin
7731 * @direction: the direction of the new transceiver
7732 * @caps: (allow none): the codec preferences for this transceiver
7734 * Returns: the new #GstWebRTCRTPTransceiver
7736 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
7737 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
7738 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7739 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
7740 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
7741 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
7744 * GstWebRTCBin::get-transceivers:
7745 * @object: the #webrtcbin
7747 * Returns: a #GArray of #GstWebRTCRTPTransceivers
7749 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
7750 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
7751 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7752 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
7756 * GstWebRTCBin::get-transceiver:
7757 * @object: the #GstWebRTCBin
7758 * @idx: The index of the transceiver
7760 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
7763 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
7764 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
7765 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7766 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
7767 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
7770 * GstWebRTCBin::add-turn-server:
7771 * @object: the #GstWebRTCBin
7772 * @uri: The uri of the server of the form turn(s)://username:password@host:port
7774 * Add a turn server to obtain ICE candidates from
7776 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
7777 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
7778 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7779 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
7780 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
7783 * GstWebRTCBin::create-data-channel:
7784 * @object: the #GstWebRTCBin
7785 * @label: the label for the data channel
7786 * @options: a #GstStructure of options for creating the data channel
7788 * The options dictionary is the same format as the RTCDataChannelInit
7789 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
7790 * and reproduced below
7792 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
7793 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
7794 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
7795 * protocol G_TYPE_STRING The subprotocol used by this channel
7796 * 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.
7797 * id G_TYPE_INT Override the default identifier selection of this channel
7798 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
7800 * Returns: (transfer full): a new data channel object
7802 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
7803 g_signal_new_class_handler ("create-data-channel",
7804 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7805 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
7806 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
7808 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
7809 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
7813 _unparent_and_unref (GObject * object)
7815 GstObject *obj = GST_OBJECT (object);
7817 GST_OBJECT_PARENT (obj) = NULL;
7819 gst_object_unref (obj);
7823 _transport_free (GObject * object)
7825 TransportStream *stream = (TransportStream *) object;
7826 GstWebRTCBin *webrtc;
7828 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
7830 if (stream->transport) {
7831 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
7832 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
7835 gst_object_unref (object);
7839 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
7841 /* Set SINK/SRC flags as webrtcbin can act as one depending on the
7842 * SDP later. Without setting this here already, surrounding bins might not
7843 * notice this and the pipeline configuration might become inconsistent,
7844 * e.g. with regards to latency.
7845 * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
7847 gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
7848 GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
7849 GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
7851 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
7852 g_mutex_init (PC_GET_LOCK (webrtc));
7853 g_cond_init (PC_GET_COND (webrtc));
7855 g_mutex_init (ICE_GET_LOCK (webrtc));
7856 g_mutex_init (DC_GET_LOCK (webrtc));
7858 webrtc->rtpbin = _create_rtpbin (webrtc);
7859 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
7861 webrtc->priv->transceivers =
7862 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
7863 webrtc->priv->transports =
7864 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
7866 webrtc->priv->data_channels =
7867 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7869 webrtc->priv->pending_data_channels =
7870 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7872 webrtc->priv->ice_stream_map =
7873 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
7874 webrtc->priv->pending_remote_ice_candidates =
7875 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7876 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
7877 (GDestroyNotify) _clear_ice_candidate_item);
7879 webrtc->priv->pending_local_ice_candidates =
7880 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7881 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
7882 (GDestroyNotify) _clear_ice_candidate_item);
7884 /* we start off closed until we move to READY */
7885 webrtc->priv->is_closed = TRUE;
7886 webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;