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 "sctptransport.h"
34 #include <gst/rtp/rtp.h>
40 #define RANDOM_SESSION_ID \
41 ((((((guint64) g_random_int()) << 32) | \
42 (guint64) g_random_int ())) & \
43 G_GUINT64_CONSTANT (0x7fffffffffffffff))
45 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
46 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
47 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
49 #define PC_GET_COND(w) (&w->priv->pc_cond)
50 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
51 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
52 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
54 #define ICE_GET_LOCK(w) (&w->priv->ice_lock)
55 #define ICE_LOCK(w) (g_mutex_lock (ICE_GET_LOCK(w)))
56 #define ICE_UNLOCK(w) (g_mutex_unlock (ICE_GET_LOCK(w)))
58 #define DC_GET_LOCK(w) (&w->priv->dc_lock)
59 #define DC_LOCK(w) (g_mutex_lock (DC_GET_LOCK(w)))
60 #define DC_UNLOCK(w) (g_mutex_unlock (DC_GET_LOCK(w)))
62 /* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */
63 #define RTPSTORAGE_EXTRA_TIME (50)
66 * SECTION: element-webrtcbin
69 * This webrtcbin implements the majority of the W3's peerconnection API and
70 * implementation guide where possible. Generating offers, answers and setting
71 * local and remote SDP's are all supported. Both media descriptions and
72 * descriptions involving data channels are supported.
74 * Each input/output pad is equivalent to a Track in W3 parlance which are
75 * added/removed from the bin. The number of requested sink pads is the number
76 * of streams that will be sent to the receiver and will be associated with a
77 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
79 * On the receiving side, RTPTransceiver's are created in response to setting
80 * a remote description. Output pads for the receiving streams in the set
81 * description are also created when data is received.
83 * A TransportStream is created when needed in order to transport the data over
84 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
85 * on the negotiated SDP's between the peers based on the bundle and rtcp
86 * configuration. Some cases are outlined below for a simple single
87 * audio/video/data session:
89 * - max-bundle uses a single transport for all
90 * media/data transported. Renegotiation involves adding/removing the
91 * necessary streams to the existing transports.
92 * - max-compat involves two TransportStream per media stream
93 * to transport the rtp and the rtcp packets and a single TransportStream for
94 * all data channels. Each stream change involves modifying the associated
95 * TransportStream/s as necessary.
100 * assert sending payload type matches the stream
101 * reconfiguration (of anything)
103 * balanced bundle policy
104 * setting custom DTLS certificates
106 * separate session id's from mlineindex properly
107 * how to deal with replacing a input/output track/stream
110 static void _update_need_negotiation (GstWebRTCBin * webrtc);
112 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
113 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
115 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
118 GST_STATIC_CAPS ("application/x-rtp"));
120 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
123 GST_STATIC_CAPS ("application/x-rtp"));
127 PROP_PAD_TRANSCEIVER = 1,
131 _have_nice_elements (GstWebRTCBin * webrtc)
133 GstPluginFeature *feature;
135 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
137 gst_object_unref (feature);
139 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
140 ("%s", "libnice elements are not available"));
144 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
146 gst_object_unref (feature);
148 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
149 ("%s", "libnice elements are not available"));
157 _have_sctp_elements (GstWebRTCBin * webrtc)
159 GstPluginFeature *feature;
161 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
163 gst_object_unref (feature);
165 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
166 ("%s", "sctp elements are not available"));
170 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
172 gst_object_unref (feature);
174 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
175 ("%s", "sctp elements are not available"));
183 _have_dtls_elements (GstWebRTCBin * webrtc)
185 GstPluginFeature *feature;
187 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
189 gst_object_unref (feature);
191 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
192 ("%s", "dtls elements are not available"));
196 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
198 gst_object_unref (feature);
200 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
201 ("%s", "dtls elements are not available"));
208 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
211 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
212 GValue * value, GParamSpec * pspec)
214 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
217 case PROP_PAD_TRANSCEIVER:
218 g_value_set_object (value, pad->trans);
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227 gst_webrtc_bin_pad_finalize (GObject * object)
229 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
232 gst_object_unref (pad->trans);
235 if (pad->received_caps)
236 gst_caps_unref (pad->received_caps);
237 pad->received_caps = NULL;
239 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
243 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
245 GObjectClass *gobject_class = (GObjectClass *) klass;
247 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
248 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
250 g_object_class_install_property (gobject_class,
251 PROP_PAD_TRANSCEIVER,
252 g_param_spec_object ("transceiver", "Transceiver",
253 "Transceiver associated with this pad",
254 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
255 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
259 gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
261 if (wpad->received_caps) {
262 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
263 GstPad *pad = GST_PAD (wpad);
266 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
267 gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
268 trans->current_ssrc, NULL));
269 gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
274 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
276 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
277 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
278 gboolean check_negotiation = FALSE;
280 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
283 gst_event_parse_caps (event, &caps);
284 check_negotiation = (!wpad->received_caps
285 || gst_caps_is_equal (wpad->received_caps, caps));
286 gst_caps_replace (&wpad->received_caps, caps);
288 GST_DEBUG_OBJECT (parent,
289 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
290 GST_PTR_FORMAT, pad, check_negotiation, caps);
292 if (check_negotiation) {
293 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
294 const GstStructure *s;
296 s = gst_caps_get_structure (caps, 0);
297 gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
298 gst_webrtc_bin_pad_update_ssrc_event (wpad);
300 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
301 check_negotiation = TRUE;
304 if (check_negotiation) {
306 _update_need_negotiation (webrtc);
310 return gst_pad_event_default (pad, parent, event);
314 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
316 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
317 gboolean ret = FALSE;
319 switch (GST_QUERY_TYPE (query)) {
320 case GST_QUERY_ACCEPT_CAPS:
321 GST_OBJECT_LOCK (wpad->trans);
322 if (wpad->trans->codec_preferences) {
325 gst_query_parse_accept_caps (query, &caps);
327 gst_query_set_accept_caps_result (query,
328 gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
331 GST_OBJECT_UNLOCK (wpad->trans);
336 GstCaps *codec_preferences = NULL;
338 GST_OBJECT_LOCK (wpad->trans);
339 if (wpad->trans->codec_preferences)
340 codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
341 GST_OBJECT_UNLOCK (wpad->trans);
343 if (codec_preferences) {
344 GstCaps *filter = NULL;
345 GstCaps *filter_prefs = NULL;
348 gst_query_parse_caps (query, &filter);
351 filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
352 GST_CAPS_INTERSECT_FIRST);
353 gst_caps_unref (codec_preferences);
355 filter_prefs = codec_preferences;
358 target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
362 result = gst_pad_query_caps (target, filter_prefs);
363 gst_query_set_caps_result (query, result);
364 gst_caps_unref (result);
366 gst_object_unref (target);
368 gst_query_set_caps_result (query, filter_prefs);
371 gst_caps_unref (filter_prefs);
383 return gst_pad_query_default (pad, parent, query);
388 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
392 static GstWebRTCBinPad *
393 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
395 GstWebRTCBinPad *pad;
396 GstPadTemplate *template;
398 if (direction == GST_PAD_SINK)
399 template = gst_static_pad_template_get (&sink_template);
400 else if (direction == GST_PAD_SRC)
401 template = gst_static_pad_template_get (&src_template);
403 g_assert_not_reached ();
406 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
407 direction, "template", template, NULL);
408 gst_object_unref (template);
410 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
411 gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
413 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
414 direction == GST_PAD_SRC ? "src" : "sink");
418 #define gst_webrtc_bin_parent_class parent_class
419 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
420 G_ADD_PRIVATE (GstWebRTCBin)
421 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
422 "webrtcbin element"););
424 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
425 GstWebRTCBinPad * pad);
431 CREATE_ANSWER_SIGNAL,
432 SET_LOCAL_DESCRIPTION_SIGNAL,
433 SET_REMOTE_DESCRIPTION_SIGNAL,
434 ADD_ICE_CANDIDATE_SIGNAL,
435 ON_NEGOTIATION_NEEDED_SIGNAL,
436 ON_ICE_CANDIDATE_SIGNAL,
437 ON_NEW_TRANSCEIVER_SIGNAL,
439 ADD_TRANSCEIVER_SIGNAL,
440 GET_TRANSCEIVER_SIGNAL,
441 GET_TRANSCEIVERS_SIGNAL,
442 ADD_TURN_SERVER_SIGNAL,
443 CREATE_DATA_CHANNEL_SIGNAL,
444 ON_DATA_CHANNEL_SIGNAL,
451 PROP_CONNECTION_STATE,
452 PROP_SIGNALING_STATE,
453 PROP_ICE_GATHERING_STATE,
454 PROP_ICE_CONNECTION_STATE,
455 PROP_LOCAL_DESCRIPTION,
456 PROP_CURRENT_LOCAL_DESCRIPTION,
457 PROP_PENDING_LOCAL_DESCRIPTION,
458 PROP_REMOTE_DESCRIPTION,
459 PROP_CURRENT_REMOTE_DESCRIPTION,
460 PROP_PENDING_REMOTE_DESCRIPTION,
464 PROP_ICE_TRANSPORT_POLICY,
469 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
474 GstWebRTCICEStream *stream;
477 /* FIXME: locking? */
479 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
483 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
484 IceStreamItem *item =
485 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
487 if (item->session_id == session_id) {
488 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
489 "session %u", item->stream, session_id);
494 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
500 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
501 GstWebRTCICEStream * stream)
503 IceStreamItem item = { session_id, stream };
505 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
506 "session %u", stream, session_id);
507 g_array_append_val (webrtc->priv->ice_stream_map, item);
510 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
513 static GstWebRTCRTPTransceiver *
514 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
515 FindTransceiverFunc func)
519 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
520 GstWebRTCRTPTransceiver *transceiver =
521 g_ptr_array_index (webrtc->priv->transceivers, i);
523 if (func (transceiver, data))
531 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
533 return g_strcmp0 (trans->mid, mid) == 0;
537 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
542 return trans->mline == *mline;
545 static GstWebRTCRTPTransceiver *
546 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
548 GstWebRTCRTPTransceiver *trans;
550 trans = _find_transceiver (webrtc, &mlineindex,
551 (FindTransceiverFunc) transceiver_match_for_mline);
553 GST_TRACE_OBJECT (webrtc,
554 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
560 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
563 static TransportStream *
564 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
565 FindTransportFunc func)
569 for (i = 0; i < webrtc->priv->transports->len; i++) {
570 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
572 if (func (stream, data))
580 match_stream_for_session (TransportStream * trans, guint * session)
582 return trans->session_id == *session;
585 static TransportStream *
586 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
588 TransportStream *stream;
590 stream = _find_transport (webrtc, &session_id,
591 (FindTransportFunc) match_stream_for_session);
593 GST_TRACE_OBJECT (webrtc,
594 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
599 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
601 static GstWebRTCBinPad *
602 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
604 GstElement *element = GST_ELEMENT (webrtc);
607 GST_OBJECT_LOCK (webrtc);
609 for (; l; l = g_list_next (l)) {
610 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
612 if (func (l->data, data)) {
613 gst_object_ref (l->data);
614 GST_OBJECT_UNLOCK (webrtc);
619 l = webrtc->priv->pending_pads;
620 for (; l; l = g_list_next (l)) {
621 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
623 if (func (l->data, data)) {
624 gst_object_ref (l->data);
625 GST_OBJECT_UNLOCK (webrtc);
629 GST_OBJECT_UNLOCK (webrtc);
634 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
637 static WebRTCDataChannel *
638 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
639 FindDataChannelFunc func)
643 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
644 WebRTCDataChannel *channel =
645 g_ptr_array_index (webrtc->priv->data_channels, i);
647 if (func (channel, data))
655 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
657 return channel->parent.id == *id;
660 /* always called with dc_lock held */
661 static WebRTCDataChannel *
662 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
664 WebRTCDataChannel *channel;
666 channel = _find_data_channel (webrtc, &id,
667 (FindDataChannelFunc) data_channel_match_for_id);
669 GST_TRACE_OBJECT (webrtc,
670 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
676 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
678 GST_OBJECT_LOCK (webrtc);
679 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
680 GST_OBJECT_UNLOCK (webrtc);
684 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
686 GST_OBJECT_LOCK (webrtc);
687 webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
688 GST_OBJECT_UNLOCK (webrtc);
692 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
694 _remove_pending_pad (webrtc, pad);
696 if (webrtc->priv->running)
697 gst_pad_set_active (GST_PAD (pad), TRUE);
698 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
702 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
704 _remove_pending_pad (webrtc, pad);
706 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
711 GstPadDirection direction;
716 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
718 return GST_PAD_DIRECTION (pad) == match->direction
719 && pad->trans->mline == match->mline;
722 static GstWebRTCBinPad *
723 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
726 MLineMatch m = { direction, mline };
728 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
733 GstPadDirection direction;
734 GstWebRTCRTPTransceiver *trans;
738 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
740 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
743 static GstWebRTCBinPad *
744 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
745 GstWebRTCRTPTransceiver * trans)
747 TransMatch m = { direction, trans };
749 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
754 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
756 return pad->ssrc == *ssrc;
760 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
767 _unlock_pc_thread (GMutex * lock)
769 g_mutex_unlock (lock);
770 return G_SOURCE_REMOVE;
774 _gst_pc_thread (GstWebRTCBin * webrtc)
777 webrtc->priv->main_context = g_main_context_new ();
778 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
780 PC_COND_BROADCAST (webrtc);
781 g_main_context_invoke (webrtc->priv->main_context,
782 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
784 /* Having the thread be the thread default GMainContext will break the
785 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
787 g_main_loop_run (webrtc->priv->loop);
789 GST_OBJECT_LOCK (webrtc);
790 g_main_context_unref (webrtc->priv->main_context);
791 webrtc->priv->main_context = NULL;
792 GST_OBJECT_UNLOCK (webrtc);
795 g_main_loop_unref (webrtc->priv->loop);
796 webrtc->priv->loop = NULL;
797 PC_COND_BROADCAST (webrtc);
804 _start_thread (GstWebRTCBin * webrtc)
809 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
810 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
814 while (!webrtc->priv->loop)
815 PC_COND_WAIT (webrtc);
816 webrtc->priv->is_closed = FALSE;
821 _stop_thread (GstWebRTCBin * webrtc)
823 GST_OBJECT_LOCK (webrtc);
824 webrtc->priv->is_closed = TRUE;
825 GST_OBJECT_UNLOCK (webrtc);
828 g_main_loop_quit (webrtc->priv->loop);
829 while (webrtc->priv->loop)
830 PC_COND_WAIT (webrtc);
833 g_thread_unref (webrtc->priv->thread);
837 _execute_op (GstWebRTCBinTask * op)
841 PC_LOCK (op->webrtc);
842 if (op->webrtc->priv->is_closed) {
843 PC_UNLOCK (op->webrtc);
847 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
848 "webrtcbin is closed. aborting execution.");
850 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
851 "error", G_TYPE_ERROR, error, NULL);
853 gst_promise_reply (op->promise, s);
855 g_clear_error (&error);
857 GST_DEBUG_OBJECT (op->webrtc,
858 "Peerconnection is closed, aborting execution");
862 s = op->op (op->webrtc, op->data);
864 PC_UNLOCK (op->webrtc);
867 gst_promise_reply (op->promise, s);
869 gst_structure_free (s);
872 return G_SOURCE_REMOVE;
876 _free_op (GstWebRTCBinTask * op)
879 op->notify (op->data);
881 gst_promise_unref (op->promise);
886 * @promise is for correctly signalling the failure case to the caller when
887 * the user supplies it. Without passing it in, the promise would never
888 * be replied to in the case that @webrtc becomes closed between the idle
889 * source addition and the the execution of the idle source.
892 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
893 gpointer data, GDestroyNotify notify, GstPromise * promise)
895 GstWebRTCBinTask *op;
899 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
901 GST_OBJECT_LOCK (webrtc);
902 if (webrtc->priv->is_closed) {
903 GST_OBJECT_UNLOCK (webrtc);
904 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
909 ctx = g_main_context_ref (webrtc->priv->main_context);
910 GST_OBJECT_UNLOCK (webrtc);
912 op = g_new0 (GstWebRTCBinTask, 1);
918 op->promise = gst_promise_ref (promise);
920 source = g_idle_source_new ();
921 g_source_set_priority (source, G_PRIORITY_DEFAULT);
922 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
923 (GDestroyNotify) _free_op);
924 g_source_attach (source, ctx);
925 g_source_unref (source);
926 g_main_context_unref (ctx);
931 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
932 static GstWebRTCICEConnectionState
933 _collate_ice_connection_states (GstWebRTCBin * webrtc)
935 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
936 GstWebRTCICEConnectionState any_state = 0;
937 gboolean all_new_or_closed = TRUE;
938 gboolean all_completed_or_closed = TRUE;
939 gboolean all_connected_completed_or_closed = TRUE;
942 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
943 GstWebRTCRTPTransceiver *rtp_trans =
944 g_ptr_array_index (webrtc->priv->transceivers, i);
945 GstWebRTCICETransport *transport;
946 GstWebRTCICEConnectionState ice_state;
948 if (rtp_trans->stopped) {
949 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
953 if (!rtp_trans->mid) {
954 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
958 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
960 /* get transport state */
961 g_object_get (transport, "state", &ice_state, NULL);
962 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
964 any_state |= (1 << ice_state);
966 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
967 all_new_or_closed = FALSE;
968 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
969 all_completed_or_closed = FALSE;
970 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
971 && ice_state != STATE (CLOSED))
972 all_connected_completed_or_closed = FALSE;
975 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
977 if (webrtc->priv->is_closed) {
978 GST_TRACE_OBJECT (webrtc, "returning closed");
979 return STATE (CLOSED);
981 /* Any of the RTCIceTransports are in the failed state. */
982 if (any_state & (1 << STATE (FAILED))) {
983 GST_TRACE_OBJECT (webrtc, "returning failed");
984 return STATE (FAILED);
986 /* Any of the RTCIceTransports are in the disconnected state. */
987 if (any_state & (1 << STATE (DISCONNECTED))) {
988 GST_TRACE_OBJECT (webrtc, "returning disconnected");
989 return STATE (DISCONNECTED);
991 /* All of the RTCIceTransports are in the new or closed state, or there are
993 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
994 GST_TRACE_OBJECT (webrtc, "returning new");
997 /* Any of the RTCIceTransports are in the checking or new state. */
998 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
999 GST_TRACE_OBJECT (webrtc, "returning checking");
1000 return STATE (CHECKING);
1002 /* All RTCIceTransports are in the completed or closed state. */
1003 if (all_completed_or_closed) {
1004 GST_TRACE_OBJECT (webrtc, "returning completed");
1005 return STATE (COMPLETED);
1007 /* All RTCIceTransports are in the connected, completed or closed state. */
1008 if (all_connected_completed_or_closed) {
1009 GST_TRACE_OBJECT (webrtc, "returning connected");
1010 return STATE (CONNECTED);
1013 GST_FIXME ("unspecified situation, returning old state");
1014 return webrtc->ice_connection_state;
1018 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1019 static GstWebRTCICEGatheringState
1020 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1022 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1023 GstWebRTCICEGatheringState any_state = 0;
1024 gboolean all_completed = webrtc->priv->transceivers->len > 0;
1027 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1028 GstWebRTCRTPTransceiver *rtp_trans =
1029 g_ptr_array_index (webrtc->priv->transceivers, i);
1030 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1031 TransportStream *stream = trans->stream;
1032 GstWebRTCDTLSTransport *dtls_transport;
1033 GstWebRTCICETransport *transport;
1034 GstWebRTCICEGatheringState ice_state;
1036 if (rtp_trans->stopped || stream == NULL) {
1037 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1042 /* We only have a mid in the transceiver after we got the SDP answer,
1043 * which is usually long after gathering has finished */
1044 if (!rtp_trans->mid) {
1045 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1048 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1049 if (dtls_transport == NULL) {
1050 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1054 transport = dtls_transport->transport;
1056 /* get gathering state */
1057 g_object_get (transport, "gathering-state", &ice_state, NULL);
1058 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1060 any_state |= (1 << ice_state);
1061 if (ice_state != STATE (COMPLETE))
1062 all_completed = FALSE;
1065 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1067 /* Any of the RTCIceTransport s are in the gathering state. */
1068 if (any_state & (1 << STATE (GATHERING))) {
1069 GST_TRACE_OBJECT (webrtc, "returning gathering");
1070 return STATE (GATHERING);
1072 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1073 * the completed gathering state. */
1074 if (all_completed) {
1075 GST_TRACE_OBJECT (webrtc, "returning complete");
1076 return STATE (COMPLETE);
1079 /* Any of the RTCIceTransport s are in the new gathering state and none
1080 * of the transports are in the gathering state, or there are no transports. */
1081 GST_TRACE_OBJECT (webrtc, "returning new");
1086 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1087 static GstWebRTCPeerConnectionState
1088 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1090 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1091 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1092 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1093 GstWebRTCICEConnectionState any_ice_state = 0;
1094 GstWebRTCDTLSTransportState any_dtls_state = 0;
1095 gboolean ice_all_new_or_closed = TRUE;
1096 gboolean dtls_all_new_or_closed = TRUE;
1097 gboolean ice_all_new_connecting_or_checking = TRUE;
1098 gboolean dtls_all_new_connecting_or_checking = TRUE;
1099 gboolean ice_all_connected_completed_or_closed = TRUE;
1100 gboolean dtls_all_connected_completed_or_closed = TRUE;
1103 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1104 GstWebRTCRTPTransceiver *rtp_trans =
1105 g_ptr_array_index (webrtc->priv->transceivers, i);
1106 GstWebRTCDTLSTransport *transport;
1107 GstWebRTCICEConnectionState ice_state;
1108 GstWebRTCDTLSTransportState dtls_state;
1110 if (rtp_trans->stopped) {
1111 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1114 if (!rtp_trans->mid) {
1115 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1119 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1121 /* get transport state */
1122 g_object_get (transport, "state", &dtls_state, NULL);
1123 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1125 any_dtls_state |= (1 << dtls_state);
1127 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1128 dtls_all_new_or_closed = FALSE;
1129 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1130 dtls_all_new_connecting_or_checking = FALSE;
1131 if (dtls_state != DTLS_STATE (CONNECTED)
1132 && dtls_state != DTLS_STATE (CLOSED))
1133 dtls_all_connected_completed_or_closed = FALSE;
1135 g_object_get (transport->transport, "state", &ice_state, NULL);
1136 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1138 any_ice_state |= (1 << ice_state);
1140 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1141 ice_all_new_or_closed = FALSE;
1142 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1143 ice_all_new_connecting_or_checking = FALSE;
1144 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1145 && ice_state != ICE_STATE (CLOSED))
1146 ice_all_connected_completed_or_closed = FALSE;
1149 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1150 "state: 0x%x", any_ice_state, any_dtls_state);
1152 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1153 if (webrtc->priv->is_closed) {
1154 GST_TRACE_OBJECT (webrtc, "returning closed");
1155 return STATE (CLOSED);
1158 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1159 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1160 GST_TRACE_OBJECT (webrtc, "returning failed");
1161 return STATE (FAILED);
1163 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1164 GST_TRACE_OBJECT (webrtc, "returning failed");
1165 return STATE (FAILED);
1168 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1170 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1171 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1172 return STATE (DISCONNECTED);
1175 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1176 * state, or there are no transports. */
1177 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1178 || webrtc->priv->transceivers->len == 0) {
1179 GST_TRACE_OBJECT (webrtc, "returning new");
1183 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1184 * or checking state. */
1185 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1186 GST_TRACE_OBJECT (webrtc, "returning connecting");
1187 return STATE (CONNECTING);
1190 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1191 * completed or closed state. */
1192 if (dtls_all_connected_completed_or_closed
1193 && ice_all_connected_completed_or_closed) {
1194 GST_TRACE_OBJECT (webrtc, "returning connected");
1195 return STATE (CONNECTED);
1198 /* FIXME: Unspecified state that happens for us */
1199 if ((dtls_all_new_connecting_or_checking
1200 || dtls_all_connected_completed_or_closed)
1201 && (ice_all_new_connecting_or_checking
1202 || ice_all_connected_completed_or_closed)) {
1203 GST_TRACE_OBJECT (webrtc, "returning connecting");
1204 return STATE (CONNECTING);
1207 GST_FIXME_OBJECT (webrtc,
1208 "Undefined situation detected, returning old state");
1209 return webrtc->peer_connection_state;
1215 static GstStructure *
1216 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1218 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1219 GstWebRTCICEGatheringState new_state;
1221 new_state = _collate_ice_gathering_states (webrtc);
1223 /* If the new state is complete, before we update the public state,
1224 * check if anyone published more ICE candidates while we were collating
1225 * and stop if so, because it means there's a new later
1226 * ice_gathering_state_task queued */
1227 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1229 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1230 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1231 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1233 ICE_UNLOCK (webrtc);
1236 if (new_state != webrtc->ice_gathering_state) {
1237 gchar *old_s, *new_s;
1239 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1241 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1243 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1244 old_s, old_state, new_s, new_state);
1248 webrtc->ice_gathering_state = new_state;
1250 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1258 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1260 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1264 static GstStructure *
1265 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1267 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1268 GstWebRTCICEConnectionState new_state;
1270 new_state = _collate_ice_connection_states (webrtc);
1272 if (new_state != old_state) {
1273 gchar *old_s, *new_s;
1275 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1277 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1279 GST_INFO_OBJECT (webrtc,
1280 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1285 webrtc->ice_connection_state = new_state;
1287 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1295 _update_ice_connection_state (GstWebRTCBin * webrtc)
1297 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1301 static GstStructure *
1302 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1304 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1305 GstWebRTCPeerConnectionState new_state;
1307 new_state = _collate_peer_connection_states (webrtc);
1309 if (new_state != old_state) {
1310 gchar *old_s, *new_s;
1312 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1314 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1316 GST_INFO_OBJECT (webrtc,
1317 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1322 webrtc->peer_connection_state = new_state;
1324 g_object_notify (G_OBJECT (webrtc), "connection-state");
1332 _update_peer_connection_state (GstWebRTCBin * webrtc)
1334 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1339 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1342 gboolean res = FALSE;
1344 GST_OBJECT_LOCK (webrtc);
1345 l = GST_ELEMENT (webrtc)->pads;
1346 for (; l; l = g_list_next (l)) {
1347 GstWebRTCBinPad *wpad;
1349 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1352 wpad = GST_WEBRTC_BIN_PAD (l->data);
1353 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1354 && (!wpad->trans || !wpad->trans->stopped)) {
1359 l = webrtc->priv->pending_pads;
1360 for (; l; l = g_list_next (l)) {
1361 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1369 GST_OBJECT_UNLOCK (webrtc);
1373 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1375 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1379 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1381 /* We can't negotiate until we have received caps on all our sink pads,
1382 * as we will need the ssrcs in our offer / answer */
1383 if (!_all_sinks_have_caps (webrtc)) {
1384 GST_LOG_OBJECT (webrtc,
1385 "no negotiation possible until caps have been received on all sink pads");
1389 /* If any implementation-specific negotiation is required, as described at
1390 * the start of this section, return "true".
1392 /* FIXME: emit when input caps/format changes? */
1394 if (!webrtc->current_local_description) {
1395 GST_LOG_OBJECT (webrtc, "no local description set");
1399 if (!webrtc->current_remote_description) {
1400 GST_LOG_OBJECT (webrtc, "no remote description set");
1404 /* If connection has created any RTCDataChannel's, and no m= section has
1405 * been negotiated yet for data, return "true". */
1406 if (webrtc->priv->data_channels->len > 0) {
1407 if (_message_get_datachannel_index (webrtc->current_local_description->
1408 sdp) >= G_MAXUINT) {
1409 GST_LOG_OBJECT (webrtc,
1410 "no data channel media section and have %u " "transports",
1411 webrtc->priv->data_channels->len);
1416 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1417 GstWebRTCRTPTransceiver *trans;
1419 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1421 if (trans->stopped) {
1422 /* FIXME: If t is stopped and is associated with an m= section according to
1423 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1424 * rejected in connection's currentLocalDescription or
1425 * currentRemoteDescription , return "true". */
1426 GST_FIXME_OBJECT (webrtc,
1427 "check if the transceiver is rejected in descriptions");
1429 const GstSDPMedia *media;
1430 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1432 if (trans->mline == -1 || trans->mid == NULL) {
1433 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1434 " mid %s", i, trans, trans->mid);
1437 /* internal inconsistency */
1438 g_assert (trans->mline <
1439 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1440 g_assert (trans->mline <
1441 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1443 /* FIXME: msid handling
1444 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1445 * section in connection's currentLocalDescription doesn't contain an
1446 * "a=msid" line, return "true". */
1449 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1451 local_dir = _get_direction_from_media (media);
1454 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1456 remote_dir = _get_direction_from_media (media);
1458 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1459 /* If connection's currentLocalDescription if of type "offer", and
1460 * the direction of the associated m= section in neither the offer
1461 * nor answer matches t's direction, return "true". */
1463 if (local_dir != trans->direction && remote_dir != trans->direction) {
1464 gchar *local_str, *remote_str, *dir_str;
1467 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1470 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1473 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1476 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1477 "description (local %s remote %s)", dir_str, local_str,
1482 g_free (remote_str);
1486 } else if (webrtc->current_local_description->type ==
1487 GST_WEBRTC_SDP_TYPE_ANSWER) {
1488 GstWebRTCRTPTransceiverDirection intersect_dir;
1490 /* If connection's currentLocalDescription if of type "answer", and
1491 * the direction of the associated m= section in the answer does not
1492 * match t's direction intersected with the offered direction (as
1493 * described in [JSEP] (section 5.3.1.)), return "true". */
1495 /* remote is the offer, local is the answer */
1496 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1498 if (intersect_dir != trans->direction) {
1499 gchar *local_str, *remote_str, *inter_str, *dir_str;
1502 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1505 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1508 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1511 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1514 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1515 "description intersected direction %s (local %s remote %s)",
1516 dir_str, local_str, inter_str, remote_str);
1520 g_free (remote_str);
1529 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1533 static GstStructure *
1534 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1536 if (webrtc->priv->need_negotiation) {
1537 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1539 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1547 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1549 _update_need_negotiation (GstWebRTCBin * webrtc)
1551 /* If connection's [[isClosed]] slot is true, abort these steps. */
1552 if (webrtc->priv->is_closed)
1554 /* If connection's signaling state is not "stable", abort these steps. */
1555 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1558 /* If the result of checking if negotiation is needed is "false", clear the
1559 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1560 * to false, and abort these steps. */
1561 if (!_check_if_negotiation_is_needed (webrtc)) {
1562 webrtc->priv->need_negotiation = FALSE;
1565 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1566 if (webrtc->priv->need_negotiation)
1568 /* Set connection's [[needNegotiation]] slot to true. */
1569 webrtc->priv->need_negotiation = TRUE;
1570 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1571 * true, fire a simple event named negotiationneeded at connection. */
1572 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1577 _find_codec_preferences (GstWebRTCBin * webrtc,
1578 GstWebRTCRTPTransceiver * rtp_trans, GstPadDirection direction,
1581 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1582 GstCaps *ret = NULL;
1583 GstCaps *codec_preferences = NULL;
1584 GstWebRTCBinPad *pad = NULL;
1585 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1589 GST_OBJECT_LOCK (rtp_trans);
1590 if (rtp_trans->codec_preferences) {
1591 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1592 rtp_trans->codec_preferences);
1593 codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
1595 GST_OBJECT_UNLOCK (rtp_trans);
1597 pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
1600 /* try to find a pad */
1602 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1605 GstCaps *caps = NULL;
1607 if (pad->received_caps) {
1608 caps = gst_caps_ref (pad->received_caps);
1609 } else if ((caps = gst_pad_get_current_caps (GST_PAD (pad)))) {
1610 GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT, caps);
1612 static GstStaticCaps static_filter =
1613 GST_STATIC_CAPS ("application/x-rtp, "
1614 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1615 GstCaps *filter = gst_static_caps_get (&static_filter);
1617 filter = gst_caps_make_writable (filter);
1619 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
1620 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
1621 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
1622 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
1624 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1625 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1627 if (!gst_caps_is_fixed (caps) || gst_caps_is_equal (caps, filter)
1628 || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
1629 gst_caps_unref (caps);
1632 gst_caps_unref (filter);
1635 if (caps && codec_preferences) {
1636 GstCaps *intersection;
1638 intersection = gst_caps_intersect_full (codec_preferences, caps,
1639 GST_CAPS_INTERSECT_FIRST);
1640 gst_caps_unref (caps);
1642 if (gst_caps_is_empty (intersection)) {
1644 gst_caps_unref (intersection);
1646 caps = intersection;
1652 gst_caps_replace (&trans->last_configured_caps, caps);
1657 gst_object_unref (pad);
1659 if (codec_preferences)
1660 ret = gst_caps_ref (codec_preferences);
1661 else if (trans && trans->last_configured_caps)
1662 ret = gst_caps_ref (trans->last_configured_caps);
1665 if (codec_preferences)
1666 gst_caps_unref (codec_preferences);
1669 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1675 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1676 WebRTCTransceiver * trans, const GstCaps * caps)
1685 ret = gst_caps_make_writable (caps);
1687 kind = webrtc_kind_from_caps (ret);
1688 for (i = 0; i < gst_caps_get_size (ret); i++) {
1689 GstStructure *s = gst_caps_get_structure (ret, i);
1692 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1693 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1695 if (kind == GST_WEBRTC_KIND_VIDEO
1696 && !gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1697 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1698 if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1699 gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
1701 /* FIXME: codec-specific parameters? */
1708 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1709 GParamSpec * pspec, GstWebRTCBin * webrtc)
1711 _update_ice_connection_state (webrtc);
1712 _update_peer_connection_state (webrtc);
1716 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1717 GParamSpec * pspec, GstWebRTCBin * webrtc)
1719 _update_ice_gathering_state (webrtc);
1723 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1724 GParamSpec * pspec, GstWebRTCBin * webrtc)
1726 _update_peer_connection_state (webrtc);
1730 match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
1732 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1734 return (trans->current_ssrc == GPOINTER_TO_UINT (data));
1738 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
1739 gboolean early, gpointer user_data)
1741 GstWebRTCBin *webrtc = user_data;
1742 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
1743 GstRTCPPacket packet;
1745 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
1748 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
1749 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
1751 GstWebRTCRTPTransceiver *rtp_trans;
1752 WebRTCTransceiver *trans;
1754 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
1757 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1759 trans = (WebRTCTransceiver *) rtp_trans;
1761 if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
1763 gchar *pad_name = NULL;
1766 g_strdup_printf ("send_rtcp_src_%u",
1767 rtp_trans->sender->transport->session_id);
1768 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
1771 gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
1772 gst_object_unref (pad);
1778 gst_rtcp_buffer_unmap (&rtcp);
1781 /* False means we don't care about suppression */
1786 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
1788 GObject *internal_session = NULL;
1790 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
1791 session_id, &internal_session);
1793 if (internal_session) {
1794 g_signal_connect (internal_session, "on-sending-rtcp",
1795 G_CALLBACK (_on_sending_rtcp), webrtc);
1796 g_object_unref (internal_session);
1800 static GstPadProbeReturn
1801 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1803 GstWebRTCBin *webrtc = user_data;
1805 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
1806 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
1807 const GstStructure *s =
1808 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
1810 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
1814 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1815 GstWebRTCRTPTransceiver *rtp_trans;
1817 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1820 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1821 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
1822 trans->stream->session_id);
1825 /* Set DSCP field based on
1826 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
1828 switch (rtp_trans->sender->priority) {
1829 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
1832 case GST_WEBRTC_PRIORITY_TYPE_LOW:
1835 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
1836 switch (rtp_trans->kind) {
1837 case GST_WEBRTC_KIND_AUDIO:
1840 case GST_WEBRTC_KIND_VIDEO:
1841 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
1843 case GST_WEBRTC_KIND_UNKNOWN:
1848 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
1849 switch (rtp_trans->kind) {
1850 case GST_WEBRTC_KIND_AUDIO:
1853 case GST_WEBRTC_KIND_VIDEO:
1854 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
1856 case GST_WEBRTC_KIND_UNKNOWN:
1863 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
1865 } else if (gst_structure_get_enum (s, "sctp-priority",
1866 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
1869 /* Set DSCP field based on
1870 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
1873 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
1876 case GST_WEBRTC_PRIORITY_TYPE_LOW:
1879 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
1880 dscp = 10; /* AF11 */
1882 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
1883 dscp = 18; /* AF21 */
1886 if (webrtc->priv->data_channel_transport)
1887 gst_webrtc_ice_set_tos (webrtc->priv->ice,
1888 webrtc->priv->data_channel_transport->stream, dscp << 2);
1892 return GST_PAD_PROBE_OK;
1895 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
1898 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
1900 GstWebRTCPriorityType sctp_priority = 0;
1903 if (!webrtc->priv->sctp_transport)
1907 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1908 GstWebRTCDataChannel *channel
1909 = g_ptr_array_index (webrtc->priv->data_channels, i);
1911 sctp_priority = MAX (sctp_priority, channel->priority);
1915 /* Default priority is low means DSCP field is left as 0 */
1916 if (sctp_priority == 0)
1917 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
1919 /* Nobody asks for DSCP, leave it as-is */
1920 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
1921 !webrtc->priv->tos_attached)
1924 /* If one stream has a non-default priority, then everyone else does too */
1925 gst_webrtc_bin_attach_tos (webrtc);
1927 gst_webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
1932 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
1933 GstWebRTCICETransport * transport)
1937 pad = gst_element_get_static_pad (transport->sink, "sink");
1938 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1939 _nicesink_pad_probe, g_object_ref (webrtc),
1940 (GDestroyNotify) gst_object_unref);
1941 gst_object_unref (pad);
1945 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
1949 if (webrtc->priv->tos_attached)
1951 webrtc->priv->tos_attached = TRUE;
1953 for (i = 0; i < webrtc->priv->transports->len; i++) {
1954 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
1956 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
1958 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
1959 stream->transport->transport);
1962 gst_webrtc_bin_update_sctp_priority (webrtc);
1965 static WebRTCTransceiver *
1966 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
1967 GstWebRTCRTPTransceiverDirection direction, guint mline)
1969 WebRTCTransceiver *trans;
1970 GstWebRTCRTPTransceiver *rtp_trans;
1971 GstWebRTCRTPSender *sender;
1972 GstWebRTCRTPReceiver *receiver;
1974 sender = gst_webrtc_rtp_sender_new ();
1975 receiver = gst_webrtc_rtp_receiver_new ();
1976 trans = webrtc_transceiver_new (webrtc, sender, receiver);
1977 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1978 rtp_trans->direction = direction;
1979 rtp_trans->mline = mline;
1980 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
1981 rtp_trans->stopped = FALSE;
1983 g_signal_connect_object (sender, "notify::priority",
1984 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
1986 g_ptr_array_add (webrtc->priv->transceivers, trans);
1988 gst_object_unref (sender);
1989 gst_object_unref (receiver);
1991 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
1997 static TransportStream *
1998 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2000 GstWebRTCDTLSTransport *transport;
2001 TransportStream *ret;
2004 /* FIXME: how to parametrize the sender and the receiver */
2005 ret = transport_stream_new (webrtc, session_id);
2006 transport = ret->transport;
2008 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2009 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2010 g_signal_connect (G_OBJECT (transport->transport),
2011 "notify::gathering-state",
2012 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2013 g_signal_connect (G_OBJECT (transport), "notify::state",
2014 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2015 if (webrtc->priv->tos_attached)
2016 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2018 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2019 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2020 g_ptr_array_add (webrtc->priv->transports, ret);
2022 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2023 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2024 GST_ELEMENT (webrtc->rtpbin), pad_name))
2025 g_warn_if_reached ();
2028 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2029 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2030 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2031 g_warn_if_reached ();
2034 GST_TRACE_OBJECT (webrtc,
2035 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2040 static TransportStream *
2041 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2043 TransportStream *ret;
2045 ret = _find_transport_for_session (webrtc, session_id);
2048 ret = _create_transport_channel (webrtc, session_id);
2050 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2051 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2056 /* this is called from the webrtc thread with the pc lock held */
2058 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2059 GParamSpec * pspec, GstWebRTCBin * webrtc)
2061 GstWebRTCDataChannelState ready_state;
2063 g_object_get (channel, "ready-state", &ready_state, NULL);
2065 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2069 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2070 if (found == FALSE) {
2071 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2076 g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2079 gst_webrtc_bin_update_sctp_priority (webrtc);
2081 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2083 } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2087 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2088 || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2090 if (found == FALSE) {
2091 GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2098 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2099 GstWebRTCBin * webrtc)
2101 WebRTCDataChannel *channel;
2105 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2109 channel = _find_data_channel_for_id (webrtc, stream_id);
2111 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2112 channel->parent.id = stream_id;
2113 channel->webrtcbin = webrtc;
2115 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
2116 gst_bin_add (GST_BIN (webrtc), channel->appsink);
2118 gst_element_sync_state_with_parent (channel->appsrc);
2119 gst_element_sync_state_with_parent (channel->appsink);
2121 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2123 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2127 g_signal_connect (channel, "notify::ready-state",
2128 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2130 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2131 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2132 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2133 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2134 gst_object_unref (sink_pad);
2138 _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
2139 GstWebRTCBin * webrtc)
2141 GstWebRTCSCTPTransportState state;
2143 g_object_get (sctp, "state", &state, NULL);
2145 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2148 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2151 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2152 WebRTCDataChannel *channel;
2154 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2156 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2158 if (!channel->parent.negotiated && !channel->opened)
2159 webrtc_data_channel_start_negotiation (channel);
2165 /* Forward declaration so we can easily disconnect the signal handler */
2166 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2167 GParamSpec * pspec, GstWebRTCBin * webrtc);
2169 static GstStructure *
2170 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2172 TransportStream *stream;
2173 GstWebRTCDTLSTransport *transport;
2174 GstWebRTCDTLSTransportState dtls_state;
2175 GstWebRTCSCTPTransport *sctp_transport;
2177 stream = webrtc->priv->data_channel_transport;
2178 transport = stream->transport;
2180 g_object_get (transport, "state", &dtls_state, NULL);
2181 /* Not connected yet so just return */
2182 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2183 GST_DEBUG_OBJECT (webrtc,
2184 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2188 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2189 sctp_transport = webrtc->priv->sctp_transport;
2191 /* Not locked state anymore so this was already taken care of before */
2192 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2195 /* Start up the SCTP elements now that the DTLS connection is established */
2196 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2197 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2199 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2200 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2202 if (sctp_transport->sctpdec_block_id) {
2203 GstPad *receive_srcpad;
2206 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2208 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2210 sctp_transport->sctpdec_block_id = 0;
2211 gst_object_unref (receive_srcpad);
2214 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2221 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2222 GParamSpec * pspec, GstWebRTCBin * webrtc)
2224 GstWebRTCDTLSTransportState dtls_state;
2226 g_object_get (transport, "state", &dtls_state, NULL);
2228 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2231 /* Connected now, so schedule a task to update the state of the SCTP
2233 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2234 gst_webrtc_bin_enqueue_task (webrtc,
2235 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2239 static GstPadProbeReturn
2240 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2242 /* Drop all events: we don't care about them and don't want to block on
2243 * them. Sticky events would be forwarded again later once we unblock
2244 * and we don't want to forward them here already because that might
2245 * cause a spurious GST_FLOW_FLUSHING */
2246 if (GST_IS_EVENT (info->data))
2247 return GST_PAD_PROBE_DROP;
2249 /* But block on any actual data-flow so we don't accidentally send that
2250 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2253 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2255 return GST_PAD_PROBE_OK;
2258 static TransportStream *
2259 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2261 if (!webrtc->priv->data_channel_transport) {
2262 TransportStream *stream;
2263 GstWebRTCSCTPTransport *sctp_transport;
2265 stream = _find_transport_for_session (webrtc, session_id);
2268 stream = _create_transport_channel (webrtc, session_id);
2270 webrtc->priv->data_channel_transport = stream;
2272 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2273 sctp_transport = gst_webrtc_sctp_transport_new ();
2274 sctp_transport->transport =
2275 g_object_ref (webrtc->priv->data_channel_transport->transport);
2276 sctp_transport->webrtcbin = webrtc;
2278 /* Don't automatically start SCTP elements as part of webrtcbin. We
2279 * need to delay this until the DTLS transport is fully connected! */
2280 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2281 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2283 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2284 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2287 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2288 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2289 g_signal_connect (sctp_transport, "notify::state",
2290 G_CALLBACK (_on_sctp_state_notify), webrtc);
2292 if (sctp_transport->sctpdec_block_id == 0) {
2293 GstPad *receive_srcpad;
2295 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2297 sctp_transport->sctpdec_block_id =
2298 gst_pad_add_probe (receive_srcpad,
2299 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2300 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2301 gst_object_unref (receive_srcpad);
2304 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2305 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2306 g_warn_if_reached ();
2308 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2309 GST_ELEMENT (stream->send_bin), "data_sink"))
2310 g_warn_if_reached ();
2312 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2313 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2315 if (!webrtc->priv->sctp_transport) {
2316 /* Connect to the notify::state signal to get notified when the DTLS
2317 * connection is established. Only then can we start the SCTP elements */
2318 g_signal_connect (stream->transport, "notify::state",
2319 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2321 /* As this would be racy otherwise, also schedule a task that checks the
2322 * current state of the connection already without getting the signal
2324 gst_webrtc_bin_enqueue_task (webrtc,
2325 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2328 webrtc->priv->sctp_transport = sctp_transport;
2330 gst_webrtc_bin_update_sctp_priority (webrtc);
2333 return webrtc->priv->data_channel_transport;
2336 static TransportStream *
2337 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2338 gboolean is_datachannel)
2341 return _get_or_create_data_channel_transports (webrtc, session_id);
2343 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2347 g_array_find_uint (GArray * array, guint val)
2351 for (i = 0; i < array->len; i++) {
2352 if (g_array_index (array, guint, i) == val)
2360 _pick_available_pt (GArray * reserved_pts, guint * i)
2362 gboolean ret = FALSE;
2364 for (*i = 96; *i <= 127; (*i)++) {
2365 if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
2366 g_array_append_val (reserved_pts, *i);
2376 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2377 GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
2378 GstSDPMedia * media)
2380 gboolean ret = TRUE;
2382 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2385 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2389 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2392 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2394 str = g_strdup_printf ("%u", pt);
2395 gst_sdp_media_add_format (media, str);
2397 str = g_strdup_printf ("%u red/%d", pt, clockrate);
2398 gst_sdp_media_add_attribute (media, "rtpmap", str);
2401 *rtx_target_pt = pt;
2403 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2406 str = g_strdup_printf ("%u", pt);
2407 gst_sdp_media_add_format (media, str);
2409 str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
2410 gst_sdp_media_add_attribute (media, "rtpmap", str);
2419 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2420 GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
2421 GstSDPMedia * media)
2423 gboolean ret = TRUE;
2425 if (trans->local_rtx_ssrc_map)
2426 gst_structure_free (trans->local_rtx_ssrc_map);
2428 trans->local_rtx_ssrc_map =
2429 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2431 if (trans->do_nack) {
2435 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2438 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2440 str = g_strdup_printf ("%u", target_ssrc);
2441 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2442 g_random_int (), NULL);
2445 str = g_strdup_printf ("%u", pt);
2446 gst_sdp_media_add_format (media, str);
2449 str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
2450 gst_sdp_media_add_attribute (media, "rtpmap", str);
2453 str = g_strdup_printf ("%u apt=%d", pt, target_pt);
2454 gst_sdp_media_add_attribute (media, "fmtp", str);
2462 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2464 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2465 GstSDPMedia * media)
2470 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2471 g_value_get_uint (value));
2472 gst_sdp_media_add_attribute (media, "ssrc-group", str);
2482 GstWebRTCBin *webrtc;
2483 WebRTCTransceiver *trans;
2487 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2493 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2494 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2495 cname = gst_structure_get_string (sdes, "cname");
2497 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2499 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2500 cname, GST_OBJECT_NAME (data->trans));
2501 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2504 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2505 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2508 gst_structure_free (sdes);
2514 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2515 WebRTCTransceiver * trans)
2518 RtxSsrcData data = { media, webrtc, trans };
2522 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2523 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2524 cname = gst_structure_get_string (sdes, "cname");
2526 if (trans->local_rtx_ssrc_map)
2527 gst_structure_foreach (trans->local_rtx_ssrc_map,
2528 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2530 for (i = 0; i < gst_caps_get_size (caps); i++) {
2531 const GstStructure *s = gst_caps_get_structure (caps, i);
2534 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2537 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2539 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2540 GST_OBJECT_NAME (trans));
2541 gst_sdp_media_add_attribute (media, "ssrc", str);
2544 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2545 gst_sdp_media_add_attribute (media, "ssrc", str);
2550 gst_structure_free (sdes);
2552 if (trans->local_rtx_ssrc_map)
2553 gst_structure_foreach (trans->local_rtx_ssrc_map,
2554 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2558 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2559 GstSDPMedia * media)
2561 gchar *cert, *fingerprint, *val;
2563 g_object_get (transport, "certificate", &cert, NULL);
2566 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2569 g_strdup_printf ("%s %s",
2570 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2571 g_free (fingerprint);
2573 gst_sdp_media_add_attribute (media, "fingerprint", val);
2577 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2579 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2580 GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
2581 GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2582 gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids)
2585 * rtp header extensions
2592 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2594 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2595 gchar *direction, *sdp_mid, *ufrag, *pwd;
2596 gboolean bundle_only;
2600 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2601 || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2604 g_assert (trans->mline == -1 || trans->mline == media_idx);
2606 bundle_only = bundled_mids && bundle_idx != media_idx
2607 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2609 /* mandated by JSEP */
2610 gst_sdp_media_add_attribute (media, "setup", "actpass");
2612 /* FIXME: deal with ICE restarts */
2613 if (last_offer && trans->mline != -1 && trans->mid) {
2614 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2615 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2616 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2618 GST_DEBUG_OBJECT (trans,
2619 "%u Generating new ice parameters mline %i, mid %s", media_idx,
2620 trans->mline, trans->mid);
2621 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2622 _generate_ice_credentials (&ufrag, &pwd);
2624 g_assert (bundle_ufrag && bundle_pwd);
2625 ufrag = g_strdup (bundle_ufrag);
2626 pwd = g_strdup (bundle_pwd);
2630 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2631 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2635 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2636 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2637 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2640 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2643 /* FIXME: negotiate this */
2644 /* FIXME: when bundle_only, these should not be added:
2645 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2646 * However, this causes incompatibilities with current versions
2647 * of the major browsers */
2648 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2649 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2652 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2654 gst_sdp_media_add_attribute (media, direction, "");
2657 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2658 caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
2660 _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
2663 g_assert_not_reached ();
2666 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2667 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2669 gst_caps_unref (caps);
2673 for (i = 0; i < gst_caps_get_size (caps); i++) {
2674 GstCaps *format = gst_caps_new_empty ();
2675 const GstStructure *s = gst_caps_get_structure (caps, i);
2677 gst_caps_append_structure (format, gst_structure_copy (s));
2679 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
2680 " to %u-th media", i, format, media_idx);
2682 /* this only looks at the first structure so we loop over the given caps
2683 * and add each structure inside it piecemeal */
2684 gst_sdp_media_set_media_from_caps (format, media);
2686 gst_caps_unref (format);
2689 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2690 const GstStructure *s = gst_caps_get_structure (caps, 0);
2691 gint clockrate = -1;
2693 gint original_rtx_target_pt; /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2694 guint rtx_target_ssrc = -1;
2696 if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
2697 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2698 g_array_append_val (reserved_pts, rtx_target_pt);
2700 original_rtx_target_pt = rtx_target_pt;
2702 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
2703 GST_WARNING_OBJECT (webrtc,
2704 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
2705 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
2706 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
2709 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2710 clockrate, &rtx_target_pt, media);
2711 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2712 clockrate, rtx_target_pt, rtx_target_ssrc, media);
2713 if (original_rtx_target_pt != rtx_target_pt)
2714 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2715 clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
2718 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
2720 /* Some identifier; we also add the media name to it so it's identifiable */
2722 gst_sdp_media_add_attribute (media, "mid", trans->mid);
2724 /* Make sure to avoid mid collisions */
2726 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2727 webrtc->priv->media_counter++);
2728 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
2731 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2732 g_hash_table_insert (all_mids, sdp_mid, NULL);
2739 * - add a=candidate lines for gathered candidates
2742 if (trans->sender) {
2743 if (!trans->sender->transport) {
2744 TransportStream *item;
2747 _get_or_create_transport_stream (webrtc,
2748 bundled_mids ? bundle_idx : media_idx, FALSE);
2750 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
2753 _add_fingerprint_to_media (trans->sender->transport, media);
2757 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2760 g_string_append_printf (bundled_mids, " %s", mid);
2763 gst_caps_unref (caps);
2769 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
2771 if (pad->received_caps) {
2772 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
2775 if (gst_structure_get_int (s, "payload", &pt)) {
2776 GST_TRACE_OBJECT (pad, "have reserved pt %u from received caps", pt);
2777 g_array_append_val (reserved_pts, pt);
2783 gather_reserved_pts (GstWebRTCBin * webrtc)
2785 GstElement *element = GST_ELEMENT (webrtc);
2786 GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2789 GST_OBJECT_LOCK (webrtc);
2790 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
2791 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
2794 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2795 GstWebRTCRTPTransceiver *trans;
2797 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
2798 GST_OBJECT_LOCK (trans);
2799 if (trans->codec_preferences) {
2803 n = gst_caps_get_size (trans->codec_preferences);
2804 for (j = 0; j < n; j++) {
2805 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
2806 if (gst_structure_get_int (s, "payload", &pt)) {
2807 GST_TRACE_OBJECT (trans, "have reserved pt %u from codec preferences",
2809 g_array_append_val (reserved_pts, pt);
2813 GST_OBJECT_UNLOCK (trans);
2815 GST_OBJECT_UNLOCK (webrtc);
2817 return reserved_pts;
2821 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
2822 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
2823 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
2825 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2826 gchar *ufrag, *pwd, *sdp_mid;
2827 gboolean bundle_only = bundled_mids
2828 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2829 && gst_sdp_message_medias_len (msg) != bundle_idx;
2830 guint last_data_index = G_MAXUINT;
2832 /* add data channel support */
2833 if (webrtc->priv->data_channels->len == 0)
2837 last_data_index = _message_get_datachannel_index (last_offer);
2838 if (last_data_index < G_MAXUINT) {
2839 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
2840 /* XXX: is this always true when recycling transceivers?
2841 * i.e. do we always put the data channel in the same mline */
2842 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
2846 /* mandated by JSEP */
2847 gst_sdp_media_add_attribute (media, "setup", "actpass");
2849 /* FIXME: only needed when restarting ICE */
2850 if (last_offer && last_data_index < G_MAXUINT) {
2851 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
2852 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
2854 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2855 _generate_ice_credentials (&ufrag, &pwd);
2857 ufrag = g_strdup (bundle_ufrag);
2858 pwd = g_strdup (bundle_pwd);
2861 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2862 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2866 gst_sdp_media_set_media (media, "application");
2867 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
2868 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2869 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2870 gst_sdp_media_add_format (media, "webrtc-datachannel");
2872 if (bundle_idx != gst_sdp_message_medias_len (msg))
2873 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2875 if (last_offer && last_data_index < G_MAXUINT) {
2876 const GstSDPMedia *last_data_media;
2879 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
2880 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
2882 gst_sdp_media_add_attribute (media, "mid", mid);
2884 /* Make sure to avoid mid collisions */
2886 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2887 webrtc->priv->media_counter++);
2888 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
2891 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2892 g_hash_table_insert (all_mids, sdp_mid, NULL);
2899 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2902 g_string_append_printf (bundled_mids, " %s", mid);
2905 /* FIXME: negotiate this properly */
2906 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2908 _get_or_create_data_channel_transports (webrtc,
2909 bundled_mids ? 0 : webrtc->priv->transceivers->len);
2910 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
2915 /* TODO: use the options argument */
2916 static GstSDPMessage *
2917 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
2920 GstSDPMessage *ret = NULL;
2921 GString *bundled_mids = NULL;
2922 gchar *bundle_ufrag = NULL;
2923 gchar *bundle_pwd = NULL;
2924 GArray *reserved_pts = NULL;
2925 GHashTable *all_mids =
2926 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2928 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2929 GList *seen_transceivers = NULL;
2930 guint media_idx = 0;
2933 gst_sdp_message_new (&ret);
2935 gst_sdp_message_set_version (ret, "0");
2938 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
2940 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
2941 sess_id = g_strdup (origin->sess_id);
2943 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
2945 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
2949 gst_sdp_message_set_session_name (ret, "-");
2950 gst_sdp_message_add_time (ret, "0", "0", NULL);
2951 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
2953 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
2954 bundled_mids = g_string_new ("BUNDLE");
2955 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
2956 bundled_mids = g_string_new ("BUNDLE");
2959 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2960 GStrv last_bundle = NULL;
2961 guint bundle_media_index;
2963 reserved_pts = gather_reserved_pts (webrtc);
2964 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
2965 && last_bundle && last_bundle && last_bundle[0]
2966 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
2968 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
2970 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
2972 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2975 g_strfreev (last_bundle);
2978 /* FIXME: recycle transceivers */
2980 /* Fill up the renegotiated streams first */
2982 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
2983 GstWebRTCRTPTransceiver *trans = NULL;
2984 const GstSDPMedia *last_media;
2986 last_media = gst_sdp_message_get_media (last_offer, i);
2988 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
2989 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
2990 const gchar *last_mid;
2992 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
2994 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2995 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
2997 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
3000 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
3002 g_assert (!g_list_find (seen_transceivers, trans));
3004 if (wtrans->mline_locked && trans->mline != media_idx) {
3005 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3006 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3007 "Previous negotiatied transceiver %"
3008 GST_PTR_FORMAT " with mid %s was in mline %d but transceiver"
3009 " has locked mline %u", trans, trans->mid, media_idx,
3014 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3015 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3016 trans->mid, media_idx);
3018 /* FIXME: deal with format changes */
3019 gst_sdp_media_copy (last_media, &media);
3020 _media_replace_direction (media, trans->direction);
3022 mid = gst_sdp_media_get_attribute_val (media, "mid");
3025 if (g_hash_table_contains (all_mids, mid)) {
3026 gst_sdp_media_free (media);
3027 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3028 GST_WEBRTC_BIN_ERROR_FAILED,
3029 "Duplicate mid %s when creating offer", mid);
3033 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3036 g_string_append_printf (bundled_mids, " %s", mid);
3038 gst_sdp_message_add_media (ret, media);
3041 gst_sdp_media_free (media);
3042 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3046 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3047 "application") == 0) {
3048 GstSDPMedia media = { 0, };
3049 gst_sdp_media_init (&media);
3050 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3051 bundle_ufrag, bundle_pwd, all_mids)) {
3052 gst_sdp_message_add_media (ret, &media);
3055 gst_sdp_media_uninit (&media);
3061 /* First, go over all transceivers and gather existing mids */
3062 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3063 GstWebRTCRTPTransceiver *trans;
3065 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3067 if (g_list_find (seen_transceivers, trans))
3071 if (g_hash_table_contains (all_mids, trans->mid)) {
3072 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
3073 "Duplicate mid %s when creating offer", trans->mid);
3077 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3082 /* add any extra streams */
3084 GstWebRTCRTPTransceiver *trans = NULL;
3085 GstSDPMedia media = { 0, };
3087 /* First find a transceiver requesting this m-line */
3088 trans = _find_transceiver_for_mline (webrtc, media_idx);
3091 /* We can't have seen it already, because it is locked to this line */
3092 g_assert (!g_list_find (seen_transceivers, trans));
3093 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3095 /* Otherwise find a free transceiver */
3096 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3097 WebRTCTransceiver *wtrans;
3099 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3100 wtrans = WEBRTC_TRANSCEIVER (trans);
3102 /* don't add transceivers twice */
3103 if (g_list_find (seen_transceivers, trans))
3106 /* Ignore transceivers with a locked mline, as they would have been
3107 * found above or will be used later */
3108 if (wtrans->mline_locked)
3111 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3112 /* don't add stopped transceivers */
3113 if (trans->stopped) {
3117 /* Otherwise take it */
3121 /* Stop if we got all transceivers */
3122 if (i == webrtc->priv->transceivers->len) {
3124 /* But try to add a data channel first, we do it here, because
3125 * it can allow a locked m-line to be put after, so we need to
3126 * do another iteration after.
3128 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3129 GstSDPMedia media = { 0, };
3130 gst_sdp_media_init (&media);
3131 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3132 bundle_ufrag, bundle_pwd, all_mids)) {
3133 gst_sdp_message_add_media (ret, &media);
3137 gst_sdp_media_uninit (&media);
3141 /* Verify that we didn't ignore any locked m-line transceivers */
3142 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3143 WebRTCTransceiver *wtrans;
3145 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3146 wtrans = WEBRTC_TRANSCEIVER (trans);
3147 /* don't add transceivers twice */
3148 if (g_list_find (seen_transceivers, trans))
3150 g_assert (wtrans->mline_locked);
3152 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3153 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3154 "Tranceiver %" GST_PTR_FORMAT " with mid %s has locked mline %d"
3155 " but the whole offer only has %u sections", trans, trans->mid,
3156 trans->mline, media_idx);
3163 gst_sdp_media_init (&media);
3165 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3166 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3169 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3170 "index %u", trans, media_idx);
3172 if (sdp_media_from_transceiver (webrtc, &media, trans,
3173 GST_WEBRTC_SDP_TYPE_OFFER, media_idx, bundled_mids, 0, bundle_ufrag,
3174 bundle_pwd, reserved_pts, all_mids)) {
3175 gst_sdp_message_add_media (ret, &media);
3178 gst_sdp_media_uninit (&media);
3181 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3182 g_array_free (reserved_pts, TRUE);
3183 reserved_pts = NULL;
3187 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3188 g_array_free (reserved_pts, TRUE);
3189 reserved_pts = NULL;
3192 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
3195 g_assert (media_idx == gst_sdp_message_medias_len (ret));
3198 gchar *mids = g_string_free (bundled_mids, FALSE);
3200 gst_sdp_message_add_attribute (ret, "group", mids);
3202 bundled_mids = NULL;
3205 /* FIXME: pre-emptively setup receiving elements when needed */
3207 if (webrtc->priv->last_generated_answer)
3208 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3209 webrtc->priv->last_generated_answer = NULL;
3210 if (webrtc->priv->last_generated_offer)
3211 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3213 GstSDPMessage *copy;
3214 gst_sdp_message_copy (ret, ©);
3215 webrtc->priv->last_generated_offer =
3216 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3221 g_array_free (reserved_pts, TRUE);
3223 g_hash_table_unref (all_mids);
3225 g_list_free (seen_transceivers);
3228 g_free (bundle_ufrag);
3231 g_free (bundle_pwd);
3234 g_string_free (bundled_mids, TRUE);
3239 gst_sdp_message_free (ret);
3245 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3246 gint * rtx_target_pt)
3250 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3253 for (i = 0; i < gst_caps_get_size (caps); i++) {
3254 const GstStructure *s = gst_caps_get_structure (caps, i);
3256 if (gst_structure_has_name (s, "application/x-rtp")) {
3257 const gchar *encoding_name =
3258 gst_structure_get_string (s, "encoding-name");
3262 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3263 gst_structure_get_int (s, "payload", &pt)) {
3264 if (!g_strcmp0 (encoding_name, "RED")) {
3267 str = g_strdup_printf ("%u", pt);
3268 gst_sdp_media_add_format (media, str);
3270 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3271 *rtx_target_pt = pt;
3272 gst_sdp_media_add_attribute (media, "rtpmap", str);
3274 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3277 str = g_strdup_printf ("%u", pt);
3278 gst_sdp_media_add_format (media, str);
3280 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3281 gst_sdp_media_add_attribute (media, "rtpmap", str);
3290 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3291 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3294 const GstStructure *s;
3296 if (trans->local_rtx_ssrc_map)
3297 gst_structure_free (trans->local_rtx_ssrc_map);
3299 trans->local_rtx_ssrc_map =
3300 gst_structure_new_empty ("application/x-rtp-ssrc-map");
3302 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
3303 s = gst_caps_get_structure (offer_caps, i);
3305 if (gst_structure_has_name (s, "application/x-rtp")) {
3306 const gchar *encoding_name =
3307 gst_structure_get_string (s, "encoding-name");
3308 const gchar *apt_str = gst_structure_get_string (s, "apt");
3316 apt = atoi (apt_str);
3318 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3319 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
3320 if (!g_strcmp0 (encoding_name, "RTX")) {
3323 str = g_strdup_printf ("%u", pt);
3324 gst_sdp_media_add_format (media, str);
3326 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
3327 gst_sdp_media_add_attribute (media, "rtpmap", str);
3330 str = g_strdup_printf ("%d apt=%d", pt, apt);
3331 gst_sdp_media_add_attribute (media, "fmtp", str);
3334 str = g_strdup_printf ("%u", target_ssrc);
3335 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
3336 g_random_int (), NULL);
3344 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
3345 const GstCaps * caps)
3347 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
3349 if (trans->kind == kind)
3352 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
3361 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
3362 guint * target_ssrc)
3364 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
3366 gst_structure_get_int (s, "payload", target_pt);
3367 gst_structure_get_uint (s, "ssrc", target_ssrc);
3370 /* TODO: use the options argument */
3371 static GstSDPMessage *
3372 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3375 GstSDPMessage *ret = NULL;
3376 const GstWebRTCSessionDescription *pending_remote =
3377 webrtc->pending_remote_description;
3379 GStrv bundled = NULL;
3380 guint bundle_idx = 0;
3381 GString *bundled_mids = NULL;
3382 gchar *bundle_ufrag = NULL;
3383 gchar *bundle_pwd = NULL;
3384 GList *seen_transceivers = NULL;
3385 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
3387 if (!webrtc->pending_remote_description) {
3388 g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
3389 GST_WEBRTC_BIN_ERROR_INVALID_STATE,
3390 "Asked to create an answer without a remote description");
3394 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
3398 GStrv last_bundle = NULL;
3399 guint bundle_media_index;
3401 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
3402 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
3403 "Bundle tag is %s but no media found matching", bundled[0]);
3407 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3408 bundled_mids = g_string_new ("BUNDLE");
3411 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
3412 && last_bundle && last_bundle[0]
3413 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
3415 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
3417 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
3419 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3422 g_strfreev (last_bundle);
3425 gst_sdp_message_new (&ret);
3427 gst_sdp_message_set_version (ret, "0");
3429 const GstSDPOrigin *offer_origin =
3430 gst_sdp_message_get_origin (pending_remote->sdp);
3431 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
3432 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
3434 gst_sdp_message_set_session_name (ret, "-");
3436 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
3437 const GstSDPAttribute *attr =
3438 gst_sdp_message_get_attribute (pending_remote->sdp, i);
3440 if (g_strcmp0 (attr->key, "ice-options") == 0) {
3441 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
3445 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
3446 GstSDPMedia *media = NULL;
3447 GstSDPMedia *offer_media;
3448 GstWebRTCDTLSSetup offer_setup, answer_setup;
3450 gboolean bundle_only;
3454 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
3455 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
3457 gst_sdp_media_new (&media);
3458 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3459 gst_sdp_media_set_port_info (media, 0, 0);
3461 gst_sdp_media_set_port_info (media, 9, 0);
3462 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3467 /* FIXME: deal with ICE restarts */
3468 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
3469 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
3470 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
3473 _generate_ice_credentials (&ufrag, &pwd);
3475 ufrag = g_strdup (bundle_ufrag);
3476 pwd = g_strdup (bundle_pwd);
3479 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3480 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3485 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
3486 const GstSDPAttribute *attr =
3487 gst_sdp_media_get_attribute (offer_media, j);
3489 if (g_strcmp0 (attr->key, "mid") == 0
3490 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
3491 gst_sdp_media_add_attribute (media, attr->key, attr->value);
3492 /* FIXME: handle anything we want to keep */
3496 mid = gst_sdp_media_get_attribute_val (media, "mid");
3497 /* XXX: not strictly required but a lot of functionality requires a mid */
3500 /* set the a=setup: attribute */
3501 offer_setup = _get_dtls_setup_from_media (offer_media);
3502 answer_setup = _intersect_dtls_setup (offer_setup);
3503 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
3504 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
3505 "transceiver direction");
3508 _media_replace_setup (media, answer_setup);
3510 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
3513 if (gst_sdp_media_formats_len (offer_media) != 1) {
3514 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
3515 "for webrtc-datachannel");
3518 sctp_port = _get_sctp_port_from_media (offer_media);
3519 if (sctp_port == -1) {
3520 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
3524 /* XXX: older browsers will produce a different SDP format for data
3525 * channel that is currently not parsed correctly */
3526 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3528 gst_sdp_media_set_media (media, "application");
3529 gst_sdp_media_set_port_info (media, 9, 0);
3530 gst_sdp_media_add_format (media, "webrtc-datachannel");
3532 /* FIXME: negotiate this properly on renegotiation */
3533 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3535 _get_or_create_data_channel_transports (webrtc,
3536 bundled_mids ? bundle_idx : i);
3540 g_string_append_printf (bundled_mids, " %s", mid);
3543 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
3545 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
3546 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
3547 GstCaps *offer_caps, *answer_caps = NULL;
3548 GstWebRTCRTPTransceiver *rtp_trans = NULL;
3549 WebRTCTransceiver *trans = NULL;
3550 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
3551 gint target_pt = -1;
3552 gint original_target_pt = -1;
3553 guint target_ssrc = 0;
3555 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3556 offer_caps = _rtp_caps_from_media (offer_media);
3558 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
3560 _find_transceiver (webrtc, mid,
3561 (FindTransceiverFunc) match_for_mid))) {
3562 const GstSDPMedia *last_media =
3563 gst_sdp_message_get_media (last_answer, i);
3564 const gchar *last_mid =
3565 gst_sdp_media_get_attribute_val (last_media, "mid");
3567 /* FIXME: assumes no shenanigans with recycling transceivers */
3568 g_assert (g_strcmp0 (mid, last_mid) == 0);
3571 && (rtp_trans->direction ==
3572 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
3573 || rtp_trans->direction ==
3574 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY))
3576 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
3578 && (rtp_trans->direction ==
3579 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
3580 || rtp_trans->direction ==
3581 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY))
3583 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SRC, i);
3585 answer_caps = _rtp_caps_from_media (last_media);
3587 /* XXX: In theory we're meant to use the sendrecv formats for the
3588 * inactive direction however we don't know what that may be and would
3589 * require asking outside what it expects to possibly send later */
3591 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
3592 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
3593 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
3595 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3596 GstCaps *trans_caps;
3598 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3600 if (g_list_find (seen_transceivers, rtp_trans)) {
3601 /* Don't double allocate a transceiver to multiple mlines */
3607 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
3609 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
3610 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
3612 /* FIXME: technically this is a little overreaching as some fields we
3613 * we can deal with not having and/or we may have unrecognized fields
3614 * that we cannot actually support */
3616 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
3617 gst_caps_unref (trans_caps);
3619 if (!gst_caps_is_empty (answer_caps)) {
3620 GST_LOG_OBJECT (webrtc,
3621 "found compatible transceiver %" GST_PTR_FORMAT
3622 " for offer media %u", rtp_trans, i);
3625 gst_caps_unref (answer_caps);
3634 answer_dir = rtp_trans->direction;
3635 g_assert (answer_caps != NULL);
3637 /* if no transceiver, then we only receive that stream and respond with
3638 * the exact same caps */
3639 /* FIXME: how to validate that subsequent elements can actually receive
3640 * this payload/format */
3641 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
3642 answer_caps = gst_caps_ref (offer_caps);
3645 if (gst_caps_is_empty (answer_caps)) {
3646 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
3648 gst_object_unref (rtp_trans);
3649 gst_caps_unref (answer_caps);
3653 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
3656 trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
3657 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
3659 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
3660 " for mline %u", trans, i);
3662 trans = WEBRTC_TRANSCEIVER (rtp_trans);
3664 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps))
3665 GST_WARNING_OBJECT (webrtc,
3666 "Trying to change transceiver %d kind from %d to %d",
3667 rtp_trans->mline, rtp_trans->kind,
3668 webrtc_kind_from_caps (answer_caps));
3670 if (!trans->do_nack) {
3671 answer_caps = gst_caps_make_writable (answer_caps);
3672 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
3673 GstStructure *s = gst_caps_get_structure (answer_caps, k);
3674 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
3678 gst_sdp_media_set_media_from_caps (answer_caps, media);
3680 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
3683 original_target_pt = target_pt;
3685 _media_add_fec (media, trans, offer_caps, &target_pt);
3686 if (trans->do_nack) {
3687 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
3688 if (target_pt != original_target_pt)
3689 _media_add_rtx (media, trans, offer_caps, original_target_pt,
3693 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
3694 _media_add_ssrcs (media, answer_caps, webrtc,
3695 WEBRTC_TRANSCEIVER (rtp_trans));
3697 gst_caps_unref (answer_caps);
3700 /* set the new media direction */
3701 offer_dir = _get_direction_from_media (offer_media);
3702 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
3703 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
3704 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
3705 "transceiver direction");
3708 _media_replace_direction (media, answer_dir);
3710 if (!trans->stream) {
3711 TransportStream *item;
3714 _get_or_create_transport_stream (webrtc,
3715 bundled_mids ? bundle_idx : i, FALSE);
3716 webrtc_transceiver_set_transport (trans, item);
3720 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3723 g_string_append_printf (bundled_mids, " %s", mid);
3726 /* set the a=fingerprint: for this transport */
3727 _add_fingerprint_to_media (trans->stream->transport, media);
3729 gst_caps_unref (offer_caps);
3731 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
3737 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
3738 gst_sdp_media_free (media);
3739 gst_sdp_media_copy (offer_media, &media);
3740 gst_sdp_media_set_port_info (media, 0, 0);
3742 gst_sdp_message_add_media (ret, media);
3743 gst_sdp_media_free (media);
3747 gchar *mids = g_string_free (bundled_mids, FALSE);
3749 gst_sdp_message_add_attribute (ret, "group", mids);
3754 g_free (bundle_ufrag);
3757 g_free (bundle_pwd);
3759 /* FIXME: can we add not matched transceivers? */
3761 /* XXX: only true for the initial offerer */
3762 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
3765 g_strfreev (bundled);
3767 g_list_free (seen_transceivers);
3769 if (webrtc->priv->last_generated_offer)
3770 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3771 webrtc->priv->last_generated_offer = NULL;
3772 if (webrtc->priv->last_generated_answer)
3773 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3775 GstSDPMessage *copy;
3776 gst_sdp_message_copy (ret, ©);
3777 webrtc->priv->last_generated_answer =
3778 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
3786 GstStructure *options;
3787 GstWebRTCSDPType type;
3790 static GstStructure *
3791 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
3793 GstWebRTCSessionDescription *desc = NULL;
3794 GstSDPMessage *sdp = NULL;
3795 GstStructure *s = NULL;
3796 GError *error = NULL;
3798 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
3799 gst_webrtc_sdp_type_to_string (data->type), data->options);
3801 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
3802 sdp = _create_offer_task (webrtc, data->options, &error);
3803 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
3804 sdp = _create_answer_task (webrtc, data->options, &error);
3806 g_assert_not_reached ();
3811 desc = gst_webrtc_session_description_new (data->type, sdp);
3812 s = gst_structure_new ("application/x-gst-promise",
3813 gst_webrtc_sdp_type_to_string (data->type),
3814 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
3816 g_warn_if_fail (error != NULL);
3817 GST_WARNING_OBJECT (webrtc, "returning error: %s",
3818 error ? error->message : "Unknown");
3819 s = gst_structure_new ("application/x-gstwebrtcbin-error",
3820 "error", G_TYPE_ERROR, error, NULL);
3821 g_clear_error (&error);
3827 gst_webrtc_session_description_free (desc);
3833 _free_create_sdp_data (struct create_sdp *data)
3836 gst_structure_free (data->options);
3841 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
3842 const GstStructure * options, GstPromise * promise)
3844 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3847 data->options = gst_structure_copy (options);
3848 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
3850 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3851 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
3853 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
3854 "Could not create offer. webrtcbin is closed");
3856 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
3857 "error", G_TYPE_ERROR, error, NULL);
3859 gst_promise_reply (promise, s);
3861 g_clear_error (&error);
3866 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
3867 const GstStructure * options, GstPromise * promise)
3869 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3872 data->options = gst_structure_copy (options);
3873 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
3875 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3876 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
3878 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
3879 "Could not create answer. webrtcbin is closed.");
3881 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
3882 "error", G_TYPE_ERROR, error, NULL);
3884 gst_promise_reply (promise, s);
3886 g_clear_error (&error);
3890 static GstWebRTCBinPad *
3891 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
3892 GstWebRTCRTPTransceiver * trans, guint serial)
3894 GstWebRTCBinPad *pad;
3897 if (direction == GST_PAD_SINK) {
3898 if (serial == G_MAXUINT)
3899 serial = webrtc->priv->max_sink_pad_serial++;
3901 serial = trans->mline;
3905 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
3907 pad = gst_webrtc_bin_pad_new (pad_name, direction);
3910 pad->trans = gst_object_ref (trans);
3915 static GstWebRTCRTPTransceiver *
3916 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
3917 const GstSDPMessage * sdp, guint media_idx)
3919 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3920 GstWebRTCRTPTransceiver *ret = NULL;
3923 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3924 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3926 if (g_strcmp0 (attr->key, "mid") == 0) {
3928 _find_transceiver (webrtc, attr->value,
3929 (FindTransceiverFunc) match_for_mid)))
3934 ret = _find_transceiver (webrtc, &media_idx,
3935 (FindTransceiverFunc) transceiver_match_for_mline);
3938 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
3943 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
3948 * ,-------------------------webrtcbin-------------------------,
3950 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3951 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3953 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3954 * ; sink_%u ; ; '---------------------' ;
3955 * o----------o send_rtp_sink_%u ; ;
3956 * ; '--------------------' ;
3957 * '--------------------- -------------------------------------'
3962 * ,--------------------------------webrtcbin--------------------------------,
3964 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3965 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3967 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3968 * ; sink_%u ,---funnel---, ; ; '---------------------' ;
3969 * o---------o sink_%u ; ; ; ;
3970 * ; sink_%u ; src o-o send_rtp_sink_%u ; ;
3971 * o---------o sink_%u ; ; ; ;
3972 * ; '------------' '--------------------' ;
3973 * '-------------------------------------------------------------------------'
3975 GstPadTemplate *rtp_templ;
3978 WebRTCTransceiver *trans;
3980 g_return_val_if_fail (pad->trans != NULL, NULL);
3982 trans = WEBRTC_TRANSCEIVER (pad->trans);
3984 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
3986 g_assert (trans->stream);
3988 if (!webrtc->rtpfunnel) {
3990 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
3991 "send_rtp_sink_%u");
3992 g_assert (rtp_templ);
3994 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
3996 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
3998 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
3999 gst_object_unref (rtp_sink);
4001 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
4002 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4003 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
4004 g_warn_if_reached ();
4007 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
4008 GstPad *funnel_sinkpad =
4009 gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
4011 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
4014 gst_object_unref (funnel_sinkpad);
4017 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
4019 return GST_PAD (pad);
4022 /* output pads are receiving elements */
4024 _connect_output_stream (GstWebRTCBin * webrtc,
4025 TransportStream * stream, guint session_id)
4028 * ,------------------------webrtcbin------------------------,
4029 * ; ,---------rtpbin---------, ;
4030 * ; ,-transport_receive_%u--, ; ; ;
4031 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
4033 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
4034 * ; '-----------------------' ; ; ; src_%u
4035 * ; ; recv_rtp_src_%u_%u_%u o--o
4036 * ; '------------------------' ;
4037 * '---------------------------------------------------------'
4041 if (stream->output_connected) {
4042 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
4043 "connected to rtpbin. Not connecting", stream);
4047 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
4048 session_id, stream);
4050 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
4051 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
4052 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
4053 g_warn_if_reached ();
4056 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
4058 /* The webrtcbin src_%u output pads will be created when rtpbin receives
4059 * data on that stream in on_rtpbin_pad_added() */
4061 stream->output_connected = TRUE;
4071 _clear_ice_candidate_item (IceCandidateItem * item)
4073 g_free (item->candidate);
4077 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
4078 gboolean drop_invalid)
4080 GstWebRTCICEStream *stream;
4082 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
4083 if (stream == NULL) {
4085 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
4088 IceCandidateItem new;
4089 new.mlineindex = item->mlineindex;
4090 new.candidate = g_strdup (item->candidate);
4091 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
4094 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
4095 ICE_UNLOCK (webrtc);
4100 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4101 item->mlineindex, item->candidate);
4103 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
4107 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
4108 const GstSDPMedia * media)
4111 GstWebRTCICEStream *stream = NULL;
4113 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
4114 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
4115 if (g_strcmp0 (attr->key, "candidate") == 0) {
4119 stream = _find_ice_stream_for_session (webrtc, mlineindex);
4120 if (stream == NULL) {
4121 GST_DEBUG_OBJECT (webrtc,
4122 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
4126 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
4127 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4128 mlineindex, candidate);
4129 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
4136 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
4137 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
4139 GstSDPMedia *media = NULL;
4141 if (mline_index < sdp->medias->len) {
4142 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
4145 if (media == NULL) {
4146 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
4150 // Add the candidate as an attribute, first stripping off the existing
4151 // candidate: key from the string description
4152 if (strlen (candidate) < 10) {
4153 GST_WARNING_OBJECT (webrtc,
4154 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
4158 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
4162 _filter_sdp_fields (GQuark field_id, const GValue * value,
4163 GstStructure * new_structure)
4165 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
4166 gst_structure_id_set_value (new_structure, field_id, value);
4172 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
4177 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4178 GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4180 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4183 for (i = 0; i < rtx_count; i++) {
4184 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4185 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4186 const gchar *apt = gst_structure_get_string (s, "apt");
4188 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4189 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4192 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4193 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4194 stream->rtxsend, pt_map);
4196 if (stream->rtxreceive)
4197 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4198 if (stream->rtxsend)
4199 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4201 gst_structure_free (pt_map);
4206 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
4207 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
4211 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4214 proto = gst_sdp_media_get_proto (media);
4215 if (proto != NULL) {
4216 /* Parse global SDP attributes once */
4217 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
4218 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
4219 gst_sdp_message_attributes_to_caps (sdp, global_caps);
4220 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
4221 gst_sdp_media_attributes_to_caps (media, global_caps);
4223 len = gst_sdp_media_formats_len (media);
4224 for (i = 0; i < len; i++) {
4225 GstCaps *caps, *outcaps;
4231 pt = atoi (gst_sdp_media_get_format (media, i));
4233 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
4236 caps = gst_sdp_media_get_caps_from_media (media, pt);
4238 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
4242 /* Merge in global caps */
4243 /* Intersect will merge in missing fields to the current caps */
4244 outcaps = gst_caps_intersect (caps, global_caps);
4245 gst_caps_unref (caps);
4247 s = gst_caps_get_structure (outcaps, 0);
4248 gst_structure_set_name (s, "application/x-rtp");
4249 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
4250 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
4252 item.caps = gst_caps_new_empty ();
4254 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
4255 GstStructure *s = gst_caps_get_structure (outcaps, j);
4256 GstStructure *filtered =
4257 gst_structure_new_empty (gst_structure_get_name (s));
4259 gst_structure_foreach (s,
4260 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
4261 gst_caps_append_structure (item.caps, filtered);
4265 gst_caps_unref (outcaps);
4267 g_array_append_val (stream->ptmap, item);
4270 gst_caps_unref (global_caps);
4275 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
4276 const GstSDPMessage * sdp, guint media_idx,
4277 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
4278 GStrv bundled, guint bundle_idx, GError ** error)
4280 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4281 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
4282 GstWebRTCRTPTransceiverDirection new_dir;
4283 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4284 GstWebRTCDTLSSetup new_setup;
4285 gboolean new_rtcp_rsize;
4286 ReceiveState receive_state = RECEIVE_STATE_UNSET;
4289 rtp_trans->mline = media_idx;
4291 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
4292 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
4293 GST_FIXME_OBJECT (webrtc,
4294 "Updating video transceiver to audio, which isn't fully supported.");
4295 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
4298 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
4299 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
4300 GST_FIXME_OBJECT (webrtc,
4301 "Updating audio transceiver to video, which isn't fully supported.");
4302 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
4305 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4306 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4308 if (g_strcmp0 (attr->key, "mid") == 0) {
4309 g_free (rtp_trans->mid);
4310 rtp_trans->mid = g_strdup (attr->value);
4315 const GstSDPMedia *local_media, *remote_media;
4316 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
4317 GstWebRTCDTLSSetup local_setup, remote_setup;
4320 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4323 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4326 local_setup = _get_dtls_setup_from_media (local_media);
4327 remote_setup = _get_dtls_setup_from_media (remote_media);
4328 new_setup = _get_final_setup (local_setup, remote_setup);
4329 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4330 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4331 "Cannot intersect direction attributes for media %u", media_idx);
4335 local_dir = _get_direction_from_media (local_media);
4336 remote_dir = _get_direction_from_media (remote_media);
4337 new_dir = _get_final_direction (local_dir, remote_dir);
4338 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4339 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4340 "Cannot intersect dtls setup attributes for media %u", media_idx);
4344 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
4345 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
4346 && prev_dir != new_dir) {
4347 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4348 GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
4349 "transceiver direction changes are not implemented. Media %u",
4354 if (!bundled || bundle_idx == media_idx) {
4355 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
4356 && _media_has_attribute_key (remote_media, "rtcp-rsize");
4360 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
4361 media_idx, &session);
4363 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
4364 g_object_unref (session);
4370 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4372 /* Not a bundled stream means this entire transport is inactive,
4373 * so set the receive state to BLOCK below */
4374 stream->active = FALSE;
4375 receive_state = RECEIVE_STATE_BLOCK;
4378 /* If this transceiver is active for sending or receiving,
4379 * we still need receive at least RTCP, so need to unblock
4380 * the receive bin below. */
4381 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
4382 receive_state = RECEIVE_STATE_PASS;
4383 stream->active = TRUE;
4386 if (new_dir != prev_dir) {
4387 gchar *prev_dir_s, *new_dir_s;
4390 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4393 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4396 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
4397 " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
4399 g_free (prev_dir_s);
4404 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4405 GstWebRTCBinPad *pad;
4407 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
4409 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4411 GstPad *peer = gst_pad_get_peer (target);
4413 gst_pad_send_event (peer, gst_event_new_eos ());
4414 gst_object_unref (peer);
4416 gst_object_unref (target);
4418 gst_object_unref (pad);
4421 /* XXX: send eos event up the sink pad as well? */
4424 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
4425 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4426 GstWebRTCBinPad *pad =
4427 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
4429 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
4430 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4431 gst_object_unref (pad);
4433 GST_DEBUG_OBJECT (webrtc,
4434 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
4435 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
4437 _connect_input_stream (webrtc, pad);
4438 _add_pad (webrtc, pad);
4441 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
4442 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4443 GstWebRTCBinPad *pad =
4444 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4446 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
4447 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4448 gst_object_unref (pad);
4450 GST_DEBUG_OBJECT (webrtc,
4451 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
4452 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
4455 if (!trans->stream) {
4456 TransportStream *item;
4459 _get_or_create_transport_stream (webrtc,
4460 bundled ? bundle_idx : media_idx, FALSE);
4461 webrtc_transceiver_set_transport (trans, item);
4464 _connect_output_stream (webrtc, trans->stream,
4465 bundled ? bundle_idx : media_idx);
4466 /* delay adding the pad until rtpbin creates the recv output pad
4467 * to ghost to so queries/events travel through the pipeline correctly
4468 * as soon as the pad is added */
4469 _add_pad_to_list (webrtc, pad);
4474 rtp_trans->mline = media_idx;
4475 rtp_trans->current_direction = new_dir;
4478 if (!bundled || bundle_idx == media_idx) {
4479 if (stream->rtxsend || stream->rtxreceive) {
4480 _set_rtx_ptmap_from_stream (webrtc, stream);
4483 g_object_set (stream, "dtls-client",
4484 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4487 /* Must be after setting the "dtls-client" so that data is not pushed into
4488 * the dtlssrtp elements before the ssl direction has been set which will
4489 * throw SSL errors */
4490 if (receive_state != RECEIVE_STATE_UNSET)
4491 transport_receive_bin_set_receive_state (stream->receive_bin,
4495 /* must be called with the pc lock held */
4497 _generate_data_channel_id (GstWebRTCBin * webrtc)
4500 gint new_id = -1, max_channels = 0;
4502 if (webrtc->priv->sctp_transport) {
4503 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
4506 if (max_channels <= 0) {
4507 max_channels = 65534;
4510 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
4513 /* TODO: a better search algorithm */
4515 WebRTCDataChannel *channel;
4519 if (new_id < 0 || new_id >= max_channels) {
4520 /* exhausted id space */
4521 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
4522 "data channel id (max %i)", max_channels);
4526 /* client must generate even ids, server must generate odd ids */
4527 if (new_id % 2 == ! !is_client)
4530 channel = _find_data_channel_for_id (webrtc, new_id);
4539 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
4540 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
4543 const GstSDPMedia *local_media, *remote_media;
4544 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
4545 TransportReceiveBin *receive;
4546 int local_port, remote_port;
4547 guint64 local_max_size, remote_max_size, max_size;
4551 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4554 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4557 local_setup = _get_dtls_setup_from_media (local_media);
4558 remote_setup = _get_dtls_setup_from_media (remote_media);
4559 new_setup = _get_final_setup (local_setup, remote_setup);
4560 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4561 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4562 "Cannot intersect dtls setup for media %u", media_idx);
4566 /* data channel is always rtcp-muxed to avoid generating ICE candidates
4568 g_object_set (stream, "dtls-client",
4569 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4571 local_port = _get_sctp_port_from_media (local_media);
4572 remote_port = _get_sctp_port_from_media (local_media);
4573 if (local_port == -1 || remote_port == -1) {
4574 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4575 "Could not find sctp port for media %u (local %i, remote %i)",
4576 media_idx, local_port, remote_port);
4580 if (0 == (local_max_size =
4581 _get_sctp_max_message_size_from_media (local_media)))
4582 local_max_size = G_MAXUINT64;
4583 if (0 == (remote_max_size =
4584 _get_sctp_max_message_size_from_media (remote_media)))
4585 remote_max_size = G_MAXUINT64;
4586 max_size = MIN (local_max_size, remote_max_size);
4588 webrtc->priv->sctp_transport->max_message_size = max_size;
4591 guint orig_local_port, orig_remote_port;
4593 /* XXX: sctpassociation warns if we are in the wrong state */
4594 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
4595 &orig_local_port, NULL);
4597 if (orig_local_port != local_port)
4598 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
4601 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
4602 &orig_remote_port, NULL);
4603 if (orig_remote_port != remote_port)
4604 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
4609 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
4610 WebRTCDataChannel *channel;
4612 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
4614 if (channel->parent.id == -1)
4615 channel->parent.id = _generate_data_channel_id (webrtc);
4616 if (channel->parent.id == -1)
4617 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
4618 ("%s", "Failed to generate an identifier for a data channel"), NULL);
4620 if (webrtc->priv->sctp_transport->association_established
4621 && !channel->parent.negotiated && !channel->opened) {
4622 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
4623 webrtc_data_channel_start_negotiation (channel);
4628 stream->active = TRUE;
4630 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
4631 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
4635 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
4638 GstWebRTCKind kind = GPOINTER_TO_INT (data);
4642 if (p1->mline != -1)
4646 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
4653 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
4656 GstPad *queue_srcpad;
4658 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
4663 if (webrtc->rtpfunnel)
4666 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
4667 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
4668 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
4670 queue = gst_element_factory_make ("queue", NULL);
4671 gst_bin_add (GST_BIN (webrtc), queue);
4672 gst_element_sync_state_with_parent (queue);
4674 gst_element_link (webrtc->rtpfunnel, queue);
4676 queue_srcpad = gst_element_get_static_pad (queue, "src");
4678 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
4679 rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
4681 gst_pad_link (queue_srcpad, rtp_sink);
4682 gst_object_unref (queue_srcpad);
4683 gst_object_unref (rtp_sink);
4685 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
4686 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4687 GST_ELEMENT (stream->send_bin), "rtp_sink"))
4688 g_warn_if_reached ();
4696 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
4697 GstWebRTCSessionDescription * sdp, GError ** error)
4700 gboolean ret = FALSE;
4701 GStrv bundled = NULL;
4702 guint bundle_idx = 0;
4703 TransportStream *bundle_stream = NULL;
4705 /* FIXME: With some peers, it's possible we could have
4706 * multiple bundles to deal with, although I've never seen one yet */
4707 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
4708 if (!_parse_bundle (sdp->sdp, &bundled, error))
4713 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
4714 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4715 "Bundle tag is %s but no media found matching", bundled[0]);
4719 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
4720 _message_media_is_datachannel (sdp->sdp, bundle_idx));
4721 /* Mark the bundle stream as inactive to start. It will be set to TRUE
4722 * by any bundled mline that is active, and at the end we set the
4723 * receivebin to BLOCK if all mlines were inactive. */
4724 bundle_stream->active = FALSE;
4726 g_array_set_size (bundle_stream->ptmap, 0);
4727 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4728 /* When bundling, we need to do this up front, or else RTX
4729 * parameters aren't set up properly for the bundled streams */
4730 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
4733 _connect_rtpfunnel (webrtc, bundle_idx);
4736 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4737 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
4738 TransportStream *stream;
4739 GstWebRTCRTPTransceiver *trans;
4740 guint transport_idx;
4742 /* skip rejected media */
4743 if (gst_sdp_media_get_port (media) == 0)
4747 transport_idx = bundle_idx;
4751 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
4753 stream = _get_or_create_transport_stream (webrtc, transport_idx,
4754 _message_media_is_datachannel (sdp->sdp, transport_idx));
4756 /* When bundling, these were all set up above, but when not
4757 * bundling we need to do it now */
4758 g_array_set_size (stream->ptmap, 0);
4759 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
4763 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
4765 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
4766 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4767 "State mismatch. Could not find local transceiver by mline %u", i);
4770 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
4771 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
4772 /* No existing transceiver, find an unused one */
4776 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
4777 kind = GST_WEBRTC_KIND_AUDIO;
4779 kind = GST_WEBRTC_KIND_VIDEO;
4781 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
4782 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
4785 /* Still no transceiver? Create one */
4786 /* XXX: default to the advertised direction in the sdp for new
4787 * transceivers. The spec doesn't actually say what happens here, only
4788 * that calls to setDirection will change the value. Nothing about
4789 * a default value when the transceiver is created internally */
4791 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
4792 _get_direction_from_media (media), i);
4793 webrtc_transceiver_set_transport (t, stream);
4794 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
4797 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
4798 trans, bundled, bundle_idx, error);
4799 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
4800 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
4803 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
4808 if (bundle_stream && bundle_stream->active == FALSE) {
4809 /* No bundled mline marked the bundle as active, so block the receive bin, as
4810 * this bundle is completely inactive */
4811 GST_LOG_OBJECT (webrtc,
4812 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
4813 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
4814 RECEIVE_STATE_BLOCK);
4820 g_strfreev (bundled);
4826 check_transceivers_not_removed (GstWebRTCBin * webrtc,
4827 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
4832 if (gst_sdp_message_medias_len (previous->sdp) >
4833 gst_sdp_message_medias_len (new->sdp))
4840 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
4845 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4846 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
4847 GstWebRTCRTPTransceiver *rtp_trans;
4848 WebRTCTransceiver *trans;
4850 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
4851 /* only look for matching mid */
4852 if (rtp_trans == NULL)
4855 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4857 /* We only validate the locked mlines for now */
4858 if (!trans->mline_locked)
4861 if (rtp_trans->mline != i) {
4862 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4863 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
4864 "m-line with mid %s is at position %d, but was locked to %d, "
4865 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
4869 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
4870 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
4871 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
4872 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4873 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
4874 "m-line %d was locked to audio, but SDP has %s media", i,
4875 gst_sdp_media_get_media (media));
4879 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
4880 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
4881 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4882 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
4883 "m-line %d was locked to video, but SDP has %s media", i,
4884 gst_sdp_media_get_media (media));
4894 struct set_description
4897 GstWebRTCSessionDescription *sdp;
4900 static GstWebRTCSessionDescription *
4901 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
4902 GstWebRTCSDPType type)
4905 case GST_WEBRTC_SDP_TYPE_OFFER:
4906 case GST_WEBRTC_SDP_TYPE_PRANSWER:
4907 case GST_WEBRTC_SDP_TYPE_ANSWER:
4908 if (source == SDP_LOCAL) {
4909 return webrtc->current_local_description;
4911 return webrtc->current_remote_description;
4913 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
4916 /* other values mean memory corruption/uninitialized! */
4917 g_assert_not_reached ();
4924 /* http://w3c.github.io/webrtc-pc/#set-description */
4925 static GstStructure *
4926 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
4928 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
4929 gboolean signalling_state_changed = FALSE;
4930 GError *error = NULL;
4931 GStrv bundled = NULL;
4932 guint bundle_idx = 0;
4936 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4937 webrtc->signaling_state);
4939 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
4940 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
4941 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
4942 _sdp_source_to_string (sd->source), type_str, state);
4943 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
4949 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
4952 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
4953 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
4957 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
4958 g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4959 "Bundle tag is %s but no matching media found", bundled[0]);
4964 if (!check_transceivers_not_removed (webrtc,
4965 get_previous_description (webrtc, sd->source, sd->sdp->type),
4967 g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR,
4968 GST_WEBRTC_BIN_ERROR_BAD_SDP,
4969 "m=lines removed from the SDP. Processing a completely new connection "
4970 "is not currently supported.");
4974 if (!check_locked_mlines (webrtc, sd->sdp, &error))
4977 switch (sd->sdp->type) {
4978 case GST_WEBRTC_SDP_TYPE_OFFER:{
4979 if (sd->source == SDP_LOCAL) {
4980 if (webrtc->pending_local_description)
4981 gst_webrtc_session_description_free
4982 (webrtc->pending_local_description);
4983 webrtc->pending_local_description =
4984 gst_webrtc_session_description_copy (sd->sdp);
4985 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
4987 if (webrtc->pending_remote_description)
4988 gst_webrtc_session_description_free
4989 (webrtc->pending_remote_description);
4990 webrtc->pending_remote_description =
4991 gst_webrtc_session_description_copy (sd->sdp);
4992 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
4996 case GST_WEBRTC_SDP_TYPE_ANSWER:{
4997 if (sd->source == SDP_LOCAL) {
4998 if (webrtc->current_local_description)
4999 gst_webrtc_session_description_free
5000 (webrtc->current_local_description);
5001 webrtc->current_local_description =
5002 gst_webrtc_session_description_copy (sd->sdp);
5004 if (webrtc->current_remote_description)
5005 gst_webrtc_session_description_free
5006 (webrtc->current_remote_description);
5007 webrtc->current_remote_description = webrtc->pending_remote_description;
5008 webrtc->pending_remote_description = NULL;
5010 if (webrtc->current_remote_description)
5011 gst_webrtc_session_description_free
5012 (webrtc->current_remote_description);
5013 webrtc->current_remote_description =
5014 gst_webrtc_session_description_copy (sd->sdp);
5016 if (webrtc->current_local_description)
5017 gst_webrtc_session_description_free
5018 (webrtc->current_local_description);
5019 webrtc->current_local_description = webrtc->pending_local_description;
5020 webrtc->pending_local_description = NULL;
5023 if (webrtc->pending_local_description)
5024 gst_webrtc_session_description_free (webrtc->pending_local_description);
5025 webrtc->pending_local_description = NULL;
5027 if (webrtc->pending_remote_description)
5028 gst_webrtc_session_description_free
5029 (webrtc->pending_remote_description);
5030 webrtc->pending_remote_description = NULL;
5032 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5035 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
5036 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
5037 if (sd->source == SDP_LOCAL) {
5038 if (webrtc->pending_local_description)
5039 gst_webrtc_session_description_free
5040 (webrtc->pending_local_description);
5041 webrtc->pending_local_description = NULL;
5043 if (webrtc->pending_remote_description)
5044 gst_webrtc_session_description_free
5045 (webrtc->pending_remote_description);
5046 webrtc->pending_remote_description = NULL;
5049 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5052 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
5053 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
5054 if (sd->source == SDP_LOCAL) {
5055 if (webrtc->pending_local_description)
5056 gst_webrtc_session_description_free
5057 (webrtc->pending_local_description);
5058 webrtc->pending_local_description =
5059 gst_webrtc_session_description_copy (sd->sdp);
5061 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
5063 if (webrtc->pending_remote_description)
5064 gst_webrtc_session_description_free
5065 (webrtc->pending_remote_description);
5066 webrtc->pending_remote_description =
5067 gst_webrtc_session_description_copy (sd->sdp);
5069 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
5075 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
5077 * If the mid value of an RTCRtpTransceiver was set to a non-null value
5078 * by the RTCSessionDescription that is being rolled back, set the mid
5079 * value of that transceiver to null, as described by [JSEP]
5080 * (section 4.1.7.2.).
5081 * If an RTCRtpTransceiver was created by applying the
5082 * RTCSessionDescription that is being rolled back, and a track has not
5083 * been attached to it via addTrack, remove that transceiver from
5084 * connection's set of transceivers, as described by [JSEP]
5085 * (section 4.1.7.2.).
5086 * Restore the value of connection's [[ sctpTransport]] internal slot
5087 * to its value at the last stable signaling state.
5091 if (webrtc->signaling_state != new_signaling_state) {
5092 webrtc->signaling_state = new_signaling_state;
5093 signalling_state_changed = TRUE;
5097 gboolean ice_controller = FALSE;
5099 /* get the current value so we don't change ice controller from TRUE to
5100 * FALSE on renegotiation or once set to TRUE for the initial local offer */
5101 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
5103 /* we control ice negotiation if we send the initial offer */
5105 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
5106 && webrtc->current_remote_description == NULL;
5107 /* or, if the remote is an ice-lite peer */
5108 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
5109 && webrtc->current_remote_description
5110 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
5113 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
5114 ice_controller ? "true" : "false");
5115 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
5118 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5121 /* media modifications */
5122 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
5125 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
5126 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
5127 GstWebRTCRTPTransceiverDirection new_dir;
5129 const GstSDPMedia *media;
5131 if (!pad->received_caps) {
5132 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
5137 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
5138 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
5143 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
5144 /* skip rejected media */
5145 if (gst_sdp_media_get_port (media) == 0) {
5146 /* FIXME: arrange for an appropriate flow return */
5147 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
5148 "a more correct flow return.");
5154 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
5159 new_dir = pad->trans->direction;
5160 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
5161 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5162 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
5163 "data at the moment. Not connecting input stream yet", pad->trans);
5168 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
5169 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
5170 pad->trans, pad->received_caps);
5171 _connect_input_stream (webrtc, pad);
5172 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
5176 gst_object_unref (old->data);
5177 webrtc->priv->pending_sink_transceivers =
5178 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
5182 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
5183 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
5185 TransportStream *item;
5188 _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
5189 _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
5191 if (sd->source == SDP_REMOTE) {
5194 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
5195 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
5197 if (g_strcmp0 (attr->key, "ssrc") == 0) {
5198 GStrv split = g_strsplit (attr->value, " ", 0);
5201 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
5202 && g_str_has_prefix (split[1], "cname:")) {
5203 g_ptr_array_add (item->remote_ssrcmap, ssrcmap_item_new (ssrc, i));
5210 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
5211 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5213 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
5214 item->stream, ufrag, pwd);
5217 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
5218 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5220 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
5221 item->stream, ufrag, pwd);
5227 if (sd->source == SDP_LOCAL) {
5228 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
5229 IceStreamItem *item =
5230 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
5232 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
5236 /* Add any pending trickle ICE candidates if we have both offer and answer */
5237 if (webrtc->current_local_description && webrtc->current_remote_description) {
5240 GstWebRTCSessionDescription *remote_sdp =
5241 webrtc->current_remote_description;
5243 /* Add any remote ICE candidates from the remote description to
5244 * support non-trickle peers first */
5245 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
5246 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
5247 _add_ice_candidates_from_sdp (webrtc, i, media);
5251 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
5252 IceCandidateItem *item =
5253 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
5254 IceCandidateItem, i);
5256 _add_ice_candidate (webrtc, item, TRUE);
5258 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
5259 ICE_UNLOCK (webrtc);
5263 * If connection's signaling state changed above, fire an event named
5264 * signalingstatechange at connection.
5266 if (signalling_state_changed) {
5267 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5268 webrtc->signaling_state);
5269 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5270 new_signaling_state);
5271 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
5274 g_object_notify (G_OBJECT (webrtc), "signaling-state");
5281 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5282 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
5284 /* If connection's signaling state is now stable, update the
5285 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
5286 * was true both before and after this update, queue a task to check
5287 * connection's [[needNegotiation]] slot and, if still true, fire a
5288 * simple event named negotiationneeded at connection.*/
5289 _update_need_negotiation (webrtc);
5290 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
5291 _check_need_negotiation_task (webrtc, NULL);
5296 g_strfreev (bundled);
5299 GstStructure *s = gst_structure_new ("application/x-gstwebrtcbin-error",
5300 "error", G_TYPE_ERROR, error, NULL);
5301 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
5302 g_clear_error (&error);
5310 _free_set_description_data (struct set_description *sd)
5313 gst_webrtc_session_description_free (sd->sdp);
5318 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
5319 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
5321 struct set_description *sd;
5323 if (remote_sdp == NULL)
5325 if (remote_sdp->sdp == NULL)
5328 sd = g_new0 (struct set_description, 1);
5329 sd->source = SDP_REMOTE;
5330 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
5332 if (!gst_webrtc_bin_enqueue_task (webrtc,
5333 (GstWebRTCBinFunc) _set_description_task, sd,
5334 (GDestroyNotify) _free_set_description_data, promise)) {
5336 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5337 "Could not set remote description. webrtcbin is closed.");
5339 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
5340 "error", G_TYPE_ERROR, error, NULL);
5342 gst_promise_reply (promise, s);
5344 g_clear_error (&error);
5351 gst_promise_reply (promise, NULL);
5352 g_return_if_reached ();
5357 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
5358 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
5360 struct set_description *sd;
5362 if (local_sdp == NULL)
5364 if (local_sdp->sdp == NULL)
5367 sd = g_new0 (struct set_description, 1);
5368 sd->source = SDP_LOCAL;
5369 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
5371 if (!gst_webrtc_bin_enqueue_task (webrtc,
5372 (GstWebRTCBinFunc) _set_description_task, sd,
5373 (GDestroyNotify) _free_set_description_data, promise)) {
5375 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5376 "Could not set remote description. webrtcbin is closed");
5378 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
5379 "error", G_TYPE_ERROR, error, NULL);
5381 gst_promise_reply (promise, s);
5383 g_clear_error (&error);
5390 gst_promise_reply (promise, NULL);
5391 g_return_if_reached ();
5395 static GstStructure *
5396 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
5398 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
5399 IceCandidateItem new;
5400 new.mlineindex = item->mlineindex;
5401 new.candidate = g_steal_pointer (&item->candidate);
5404 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5405 ICE_UNLOCK (webrtc);
5407 _add_ice_candidate (webrtc, item, FALSE);
5414 _free_ice_candidate_item (IceCandidateItem * item)
5416 _clear_ice_candidate_item (item);
5421 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
5424 IceCandidateItem *item;
5426 item = g_new0 (IceCandidateItem, 1);
5427 item->mlineindex = mline;
5428 if (attr && attr[0] != 0) {
5429 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
5430 item->candidate = g_strdup (attr);
5431 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
5432 item->candidate = g_strdup_printf ("a=%s", attr);
5434 gst_webrtc_bin_enqueue_task (webrtc,
5435 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
5436 (GDestroyNotify) _free_ice_candidate_item, NULL);
5439 static GstStructure *
5440 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
5446 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
5447 ICE_UNLOCK (webrtc);
5448 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
5449 return NULL; /* Nothing to process */
5451 /* Take the array so we can process it all and free it later
5452 * without holding the lock
5453 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
5455 items = webrtc->priv->pending_local_ice_candidates;
5456 /* Replace with a new array */
5457 webrtc->priv->pending_local_ice_candidates =
5458 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
5459 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
5460 (GDestroyNotify) _clear_ice_candidate_item);
5461 ICE_UNLOCK (webrtc);
5463 for (i = 0; i < items->len; i++) {
5464 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
5465 const gchar *cand = item->candidate;
5467 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
5468 /* stripping away "a=" */
5472 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
5473 item->mlineindex, cand);
5475 /* First, merge this ice candidate into the appropriate mline
5476 * in the local-description SDP.
5477 * Second, emit the on-ice-candidate signal for the app.
5479 * FIXME: This ICE candidate should be stored somewhere with
5480 * the associated mid and also merged back into any subsequent
5481 * local descriptions on renegotiation */
5482 if (webrtc->current_local_description)
5483 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
5484 item->mlineindex, cand);
5485 if (webrtc->pending_local_description)
5486 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
5487 item->mlineindex, cand);
5490 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
5491 0, item->mlineindex, cand);
5495 g_array_free (items, TRUE);
5501 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
5502 gchar * candidate, GstWebRTCBin * webrtc)
5504 IceCandidateItem item;
5505 gboolean queue_task = FALSE;
5507 item.mlineindex = session_id;
5508 item.candidate = g_strdup (candidate);
5511 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
5513 /* Let the first pending candidate queue a task each time, which will
5514 * handle any that arrive between now and when the task runs */
5515 if (webrtc->priv->pending_local_ice_candidates->len == 1)
5517 ICE_UNLOCK (webrtc);
5520 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
5521 gst_webrtc_bin_enqueue_task (webrtc,
5522 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
5529 GstPromise *promise;
5533 _free_get_stats (struct get_stats *stats)
5536 gst_object_unref (stats->pad);
5538 gst_promise_unref (stats->promise);
5542 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
5543 static GstStructure *
5544 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
5546 /* Our selector is the pad,
5547 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
5550 return gst_webrtc_bin_create_stats (webrtc, stats->pad);
5554 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
5555 GstPromise * promise)
5557 struct get_stats *stats;
5559 g_return_if_fail (promise != NULL);
5560 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
5562 stats = g_new0 (struct get_stats, 1);
5563 stats->promise = gst_promise_ref (promise);
5564 /* FIXME: check that pad exists in element */
5566 stats->pad = gst_object_ref (pad);
5568 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
5569 stats, (GDestroyNotify) _free_get_stats, promise)) {
5571 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5572 "Could not retrieve statistics. webrtcbin is closed.");
5573 GstStructure *s = gst_structure_new ("application/x-gst-promise-error",
5574 "error", G_TYPE_ERROR, error, NULL);
5576 gst_promise_reply (promise, s);
5578 g_clear_error (&error);
5582 static GstWebRTCRTPTransceiver *
5583 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
5584 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
5586 WebRTCTransceiver *trans;
5587 GstWebRTCRTPTransceiver *rtp_trans;
5589 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
5594 trans = _create_webrtc_transceiver (webrtc, direction, -1);
5595 GST_LOG_OBJECT (webrtc,
5596 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
5598 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
5600 GST_OBJECT_LOCK (trans);
5601 rtp_trans->codec_preferences = gst_caps_ref (caps);
5602 GST_OBJECT_UNLOCK (trans);
5603 _update_transceiver_kind_from_caps (rtp_trans, caps);
5608 return gst_object_ref (trans);
5612 _deref_and_unref (GstObject ** object)
5614 gst_clear_object (object);
5618 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
5620 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
5625 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
5627 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5628 GstWebRTCRTPTransceiver *trans =
5629 g_ptr_array_index (webrtc->priv->transceivers, i);
5630 gst_object_ref (trans);
5631 g_array_append_val (arr, trans);
5638 static GstWebRTCRTPTransceiver *
5639 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
5641 GstWebRTCRTPTransceiver *trans = NULL;
5645 if (idx >= webrtc->priv->transceivers->len) {
5646 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
5650 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
5651 gst_object_ref (trans);
5659 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
5663 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
5664 g_return_val_if_fail (uri != NULL, FALSE);
5666 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
5669 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
5676 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
5678 GstPad *gpad = GST_PAD_CAST (user_data);
5680 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
5681 gst_pad_store_sticky_event (gpad, *event);
5686 static WebRTCDataChannel *
5687 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
5688 GstStructure * init_params)
5691 gint max_packet_lifetime;
5692 gint max_retransmits;
5693 const gchar *protocol;
5694 gboolean negotiated;
5696 GstWebRTCPriorityType priority;
5697 WebRTCDataChannel *ret;
5698 gint max_channels = 65534;
5700 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
5701 g_return_val_if_fail (label != NULL, NULL);
5702 g_return_val_if_fail (strlen (label) <= 65535, NULL);
5703 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
5706 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
5709 || !gst_structure_get_int (init_params, "max-packet-lifetime",
5710 &max_packet_lifetime))
5711 max_packet_lifetime = -1;
5713 || !gst_structure_get_int (init_params, "max-retransmits",
5715 max_retransmits = -1;
5716 /* both retransmits and lifetime cannot be set */
5717 g_return_val_if_fail ((max_packet_lifetime == -1)
5718 || (max_retransmits == -1), NULL);
5721 || !(protocol = gst_structure_get_string (init_params, "protocol")))
5723 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
5726 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
5728 if (!negotiated || !init_params
5729 || !gst_structure_get_int (init_params, "id", &id))
5732 g_return_val_if_fail (id != -1, NULL);
5733 g_return_val_if_fail (id < 65535, NULL);
5736 || !gst_structure_get_enum (init_params, "priority",
5737 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
5738 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
5740 /* FIXME: clamp max-retransmits and max-packet-lifetime */
5742 if (webrtc->priv->sctp_transport) {
5743 /* Let transport be the connection's [[SctpTransport]] slot.
5745 * If the [[DataChannelId]] slot is not null, transport is in
5746 * connected state and [[DataChannelId]] is greater or equal to the
5747 * transport's [[MaxChannels]] slot, throw an OperationError.
5749 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5752 g_return_val_if_fail (id <= max_channels, NULL);
5755 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
5756 !_have_sctp_elements (webrtc))
5761 /* check if the id has been used already */
5763 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
5765 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
5766 ("Attempting to add a data channel with a duplicate ID: %i", id),
5772 } else if (webrtc->current_local_description
5773 && webrtc->current_remote_description && webrtc->priv->sctp_transport
5774 && webrtc->priv->sctp_transport->transport) {
5775 /* else we can only generate an id if we're configured already. The other
5776 * case for generating an id is on sdp setting */
5777 id = _generate_data_channel_id (webrtc);
5779 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5780 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5787 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
5788 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
5789 "max-retransmits", max_retransmits, "protocol", protocol,
5790 "negotiated", negotiated, "id", id, "priority", priority, NULL);
5798 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
5799 gst_bin_add (GST_BIN (webrtc), ret->appsink);
5801 gst_element_sync_state_with_parent (ret->appsrc);
5802 gst_element_sync_state_with_parent (ret->appsink);
5804 ret = gst_object_ref (ret);
5805 ret->webrtcbin = webrtc;
5806 g_ptr_array_add (webrtc->priv->data_channels, ret);
5809 gst_webrtc_bin_update_sctp_priority (webrtc);
5810 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
5811 if (webrtc->priv->sctp_transport &&
5812 webrtc->priv->sctp_transport->association_established
5813 && !ret->parent.negotiated) {
5814 webrtc_data_channel_start_negotiation (ret);
5816 _update_need_negotiation (webrtc);
5823 /* === rtpbin signal implementations === */
5826 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
5827 GstWebRTCBin * webrtc)
5829 gchar *new_pad_name = NULL;
5831 new_pad_name = gst_pad_get_name (new_pad);
5832 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
5833 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
5834 guint32 session_id = 0, ssrc = 0, pt = 0;
5835 GstWebRTCRTPTransceiver *rtp_trans;
5836 WebRTCTransceiver *trans;
5837 TransportStream *stream;
5838 GstWebRTCBinPad *pad;
5839 guint media_idx = 0;
5840 gboolean found_ssrc = FALSE;
5843 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
5845 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
5849 stream = _find_transport_for_session (webrtc, session_id);
5851 g_warn_if_reached ();
5853 media_idx = session_id;
5855 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
5856 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
5857 if (item->ssrc == ssrc) {
5858 media_idx = item->media_idx;
5865 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
5868 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
5870 g_warn_if_reached ();
5871 trans = WEBRTC_TRANSCEIVER (rtp_trans);
5872 g_assert (trans->stream == stream);
5874 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5876 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
5877 " for rtpbin pad name %s", pad, new_pad_name);
5879 g_warn_if_reached ();
5880 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
5882 if (webrtc->priv->running)
5883 gst_pad_set_active (GST_PAD (pad), TRUE);
5884 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
5885 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
5886 _remove_pending_pad (webrtc, pad);
5888 gst_object_unref (pad);
5890 g_free (new_pad_name);
5893 /* only used for the receiving streams */
5895 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
5896 GstWebRTCBin * webrtc)
5898 TransportStream *stream;
5901 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
5904 stream = _find_transport_for_session (webrtc, session_id);
5906 goto unknown_session;
5908 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
5911 GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
5912 "session %d", ret, pt, session_id);
5918 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
5924 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
5926 GstStructure *s = user_data;
5928 gst_structure_id_set_value (s, field_id, value);
5934 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
5935 GstWebRTCBin * webrtc)
5937 TransportStream *stream;
5938 gboolean have_rtx = FALSE;
5939 GstStructure *pt_map = NULL;
5940 GstElement *ret = NULL;
5942 stream = _find_transport_for_session (webrtc, session_id);
5945 have_rtx = transport_stream_get_pt (stream, "RTX") != 0;
5947 GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
5948 " with pt map %" GST_PTR_FORMAT, stream, pt_map);
5954 GstStructure *merged_local_rtx_ssrc_map =
5955 gst_structure_new_empty ("application/x-rtp-ssrc-map");
5958 if (stream->rtxsend) {
5959 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
5963 GST_INFO ("creating AUX sender");
5964 ret = gst_bin_new (NULL);
5965 rtx = gst_element_factory_make ("rtprtxsend", NULL);
5966 g_object_set (rtx, "max-size-packets", 500, NULL);
5967 _set_rtx_ptmap_from_stream (webrtc, stream);
5969 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5970 WebRTCTransceiver *trans =
5971 WEBRTC_TRANSCEIVER (g_ptr_array_index (webrtc->priv->transceivers,
5974 if (trans->stream == stream && trans->local_rtx_ssrc_map)
5975 gst_structure_foreach (trans->local_rtx_ssrc_map,
5976 _merge_structure, merged_local_rtx_ssrc_map);
5979 g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
5980 gst_structure_free (merged_local_rtx_ssrc_map);
5982 gst_bin_add (GST_BIN (ret), rtx);
5984 pad = gst_element_get_static_pad (rtx, "src");
5985 name = g_strdup_printf ("src_%u", session_id);
5986 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
5988 gst_object_unref (pad);
5990 pad = gst_element_get_static_pad (rtx, "sink");
5991 name = g_strdup_printf ("sink_%u", session_id);
5992 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
5994 gst_object_unref (pad);
5996 stream->rtxsend = gst_object_ref (rtx);
6001 gst_structure_free (pt_map);
6007 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
6008 GstWebRTCBin * webrtc)
6010 GstElement *ret = NULL;
6011 GstElement *prev = NULL;
6012 GstPad *sinkpad = NULL;
6013 TransportStream *stream;
6017 stream = _find_transport_for_session (webrtc, session_id);
6020 red_pt = transport_stream_get_pt (stream, "RED");
6021 rtx_pt = transport_stream_get_pt (stream, "RTX");
6024 GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
6027 if (red_pt || rtx_pt)
6028 ret = gst_bin_new (NULL);
6031 if (stream->rtxreceive) {
6032 GST_WARNING_OBJECT (webrtc,
6033 "rtprtxreceive already created! rtpbin bug?!");
6037 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
6038 _set_rtx_ptmap_from_stream (webrtc, stream);
6040 gst_bin_add (GST_BIN (ret), stream->rtxreceive);
6042 sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
6044 prev = gst_object_ref (stream->rtxreceive);
6048 GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
6050 GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
6051 red_pt, session_id);
6053 gst_bin_add (GST_BIN (ret), rtpreddec);
6055 g_object_set (rtpreddec, "pt", red_pt, NULL);
6058 gst_element_link (prev, rtpreddec);
6060 sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
6066 gchar *name = g_strdup_printf ("sink_%u", session_id);
6067 GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
6069 gst_object_unref (sinkpad);
6070 gst_element_add_pad (ret, ghost);
6074 gchar *name = g_strdup_printf ("src_%u", session_id);
6075 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
6076 GstPad *ghost = gst_ghost_pad_new (name, srcpad);
6078 gst_object_unref (srcpad);
6079 gst_element_add_pad (ret, ghost);
6087 gst_object_unref (ret);
6092 on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
6093 GstWebRTCBin * webrtc)
6095 TransportStream *stream;
6096 GstElement *ret = NULL;
6098 GObject *internal_storage;
6100 stream = _find_transport_for_session (webrtc, session_id);
6102 /* TODO: for now, we only support ulpfec, but once we support
6103 * more algorithms, if the remote may use more than one algorithm,
6104 * we will want to do the following:
6106 * + Return a bin here, with the relevant FEC decoders plugged in
6107 * and their payload type set to 0
6108 * + Enable the decoders by setting the payload type only when
6109 * we detect it (by connecting to ptdemux:new-payload-type for
6113 pt = transport_stream_get_pt (stream, "ULPFEC");
6116 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
6118 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
6119 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
6122 g_object_set (ret, "pt", pt, "storage", internal_storage, NULL);
6123 g_object_unref (internal_storage);
6130 on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
6131 GstWebRTCBin * webrtc)
6133 GstElement *ret = NULL;
6134 GstElement *prev = NULL;
6135 TransportStream *stream;
6136 guint ulpfec_pt = 0;
6138 GstPad *sinkpad = NULL;
6139 GstWebRTCRTPTransceiver *trans;
6141 stream = _find_transport_for_session (webrtc, session_id);
6142 trans = _find_transceiver (webrtc, &session_id,
6143 (FindTransceiverFunc) transceiver_match_for_mline);
6146 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
6147 red_pt = transport_stream_get_pt (stream, "RED");
6150 if (ulpfec_pt || red_pt)
6151 ret = gst_bin_new (NULL);
6154 GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
6155 GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
6157 GST_DEBUG_OBJECT (webrtc,
6158 "Creating ULPFEC encoder for session %d with pt %d", session_id,
6161 gst_bin_add (GST_BIN (ret), fecenc);
6162 sinkpad = gst_element_get_static_pad (fecenc, "sink");
6163 g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
6164 WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
6167 if (caps && !gst_caps_is_empty (caps)) {
6168 const GstStructure *s = gst_caps_get_structure (caps, 0);
6169 const gchar *media = gst_structure_get_string (s, "media");
6171 if (!g_strcmp0 (media, "video"))
6172 g_object_set (fecenc, "multipacket", TRUE, NULL);
6179 GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
6181 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
6182 session_id, red_pt);
6184 gst_bin_add (GST_BIN (ret), redenc);
6186 gst_element_link (prev, redenc);
6188 sinkpad = gst_element_get_static_pad (redenc, "sink");
6190 g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
6196 GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
6197 gst_object_unref (sinkpad);
6198 gst_element_add_pad (ret, ghost);
6202 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
6203 GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
6204 gst_object_unref (srcpad);
6205 gst_element_add_pad (ret, ghost);
6212 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6213 GstWebRTCBin * webrtc)
6215 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
6219 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6220 GstWebRTCBin * webrtc)
6222 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
6226 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6227 GstWebRTCBin * webrtc)
6229 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
6234 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6235 GstWebRTCBin * webrtc)
6237 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
6241 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6242 GstWebRTCBin * webrtc)
6244 GST_INFO_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
6248 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
6249 GstWebRTCBin * webrtc)
6251 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
6255 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
6256 GstWebRTCBin * webrtc)
6258 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
6262 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
6263 GstWebRTCBin * webrtc)
6265 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
6269 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6270 GstWebRTCBin * webrtc)
6272 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
6276 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6277 GstWebRTCBin * webrtc)
6279 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
6284 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6285 GstWebRTCBin * webrtc)
6287 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
6292 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
6293 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
6295 WebRTCTransceiver *trans;
6298 trans = (WebRTCTransceiver *) _find_transceiver (webrtc, &session_id,
6299 (FindTransceiverFunc) transceiver_match_for_mline);
6302 /* We don't set do-retransmission on rtpbin as we want per-session control */
6303 g_object_set (jitterbuffer, "do-retransmission",
6304 WEBRTC_TRANSCEIVER (trans)->do_nack, NULL);
6306 for (i = 0; i < trans->stream->remote_ssrcmap->len; i++) {
6307 SsrcMapItem *item = g_ptr_array_index (trans->stream->remote_ssrcmap, i);
6309 if (item->ssrc == ssrc) {
6310 g_weak_ref_set (&item->rtpjitterbuffer, jitterbuffer);
6315 g_assert_not_reached ();
6320 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
6321 guint session_id, GstWebRTCBin * webrtc)
6323 guint64 latency = webrtc->priv->jb_latency;
6325 /* Add an extra 50 ms for safey */
6326 latency += RTPSTORAGE_EXTRA_TIME;
6327 latency *= GST_MSECOND;
6329 g_object_set (storage, "size-time", latency, NULL);
6333 _create_rtpbin (GstWebRTCBin * webrtc)
6337 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
6340 /* mandated by WebRTC */
6341 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
6343 g_object_set (rtpbin, "do-lost", TRUE, NULL);
6345 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
6347 g_signal_connect (rtpbin, "request-pt-map",
6348 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
6349 g_signal_connect (rtpbin, "request-aux-sender",
6350 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
6351 g_signal_connect (rtpbin, "request-aux-receiver",
6352 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
6353 g_signal_connect (rtpbin, "new-storage",
6354 G_CALLBACK (on_rtpbin_new_storage), webrtc);
6355 g_signal_connect (rtpbin, "request-fec-decoder",
6356 G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
6357 g_signal_connect (rtpbin, "request-fec-encoder",
6358 G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
6359 g_signal_connect (rtpbin, "on-bye-ssrc",
6360 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
6361 g_signal_connect (rtpbin, "on-bye-timeout",
6362 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
6363 g_signal_connect (rtpbin, "on-new-ssrc",
6364 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
6365 g_signal_connect (rtpbin, "on-new-sender-ssrc",
6366 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
6367 g_signal_connect (rtpbin, "on-sender-ssrc-active",
6368 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
6369 g_signal_connect (rtpbin, "on-sender-timeout",
6370 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
6371 g_signal_connect (rtpbin, "on-ssrc-active",
6372 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
6373 g_signal_connect (rtpbin, "on-ssrc-collision",
6374 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
6375 g_signal_connect (rtpbin, "on-ssrc-sdes",
6376 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
6377 g_signal_connect (rtpbin, "on-ssrc-validated",
6378 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
6379 g_signal_connect (rtpbin, "on-timeout",
6380 G_CALLBACK (on_rtpbin_timeout), webrtc);
6381 g_signal_connect (rtpbin, "new-jitterbuffer",
6382 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
6387 static GstStateChangeReturn
6388 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
6390 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6391 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
6393 GST_DEBUG ("changing state: %s => %s",
6394 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
6395 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
6397 switch (transition) {
6398 case GST_STATE_CHANGE_NULL_TO_READY:{
6399 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6400 return GST_STATE_CHANGE_FAILURE;
6401 _start_thread (webrtc);
6403 _update_need_negotiation (webrtc);
6407 case GST_STATE_CHANGE_READY_TO_PAUSED:
6408 webrtc->priv->running = TRUE;
6414 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6415 if (ret == GST_STATE_CHANGE_FAILURE)
6418 switch (transition) {
6419 case GST_STATE_CHANGE_READY_TO_PAUSED:
6420 /* Mangle the return value to NO_PREROLL as that's what really is
6421 * occurring here however cannot be propagated correctly due to nicesrc
6422 * requiring that it be in PLAYING already in order to send/receive
6424 ret = GST_STATE_CHANGE_NO_PREROLL;
6426 case GST_STATE_CHANGE_PAUSED_TO_READY:
6427 webrtc->priv->running = FALSE;
6429 case GST_STATE_CHANGE_READY_TO_NULL:
6430 _stop_thread (webrtc);
6439 static GstPadProbeReturn
6440 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
6442 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
6444 return GST_PAD_PROBE_OK;
6449 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
6450 const gchar * name, const GstCaps * caps)
6452 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6453 GstWebRTCRTPTransceiver *trans = NULL;
6454 GstWebRTCBinPad *pad = NULL;
6456 gboolean lock_mline = FALSE;
6458 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6461 if (templ->direction != GST_PAD_SINK ||
6462 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
6463 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
6469 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
6470 /* no name given when requesting the pad, use next available int */
6471 serial = webrtc->priv->max_sink_pad_serial++;
6473 /* parse serial number from requested padname */
6474 serial = g_ascii_strtoull (&name[5], NULL, 10);
6479 GstWebRTCBinPad *pad2;
6481 trans = _find_transceiver_for_mline (webrtc, serial);
6484 /* Reject transceivers that are only for receiving ... */
6485 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
6486 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
6488 g_enum_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
6490 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6491 " existing m-line %d, but the transceiver's direction is %s",
6492 name, serial, direction);
6497 /* Reject transceivers that already have a pad allocated */
6498 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
6500 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
6501 " but the transceiver associated with this m-line already has pad"
6502 " %s", name, serial, GST_PAD_NAME (pad2));
6503 gst_object_unref (pad2);
6508 GST_OBJECT_LOCK (trans);
6509 if (trans->codec_preferences &&
6510 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
6511 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6512 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
6513 " don't match existing codec preferences %" GST_PTR_FORMAT,
6514 name, serial, caps, trans->codec_preferences);
6515 GST_OBJECT_UNLOCK (trans);
6518 GST_OBJECT_UNLOCK (trans);
6520 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
6521 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
6523 if (trans->kind != kind) {
6524 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6525 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
6526 " don't match transceiver kind %d",
6527 name, serial, caps, trans->kind);
6535 /* Let's try to find a free transceiver that matches */
6537 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
6541 kind = webrtc_kind_from_caps (caps);
6543 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6544 GstWebRTCRTPTransceiver *tmptrans =
6545 g_ptr_array_index (webrtc->priv->transceivers, i);
6546 GstWebRTCBinPad *pad2;
6547 gboolean has_matching_caps;
6549 /* Ignore transceivers with a non-matching kind */
6550 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
6551 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
6554 /* Ignore stopped transmitters */
6555 if (tmptrans->stopped)
6558 /* Ignore transceivers that are only for receiving ... */
6559 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
6560 || tmptrans->direction ==
6561 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
6564 /* Ignore transceivers that already have a pad allocated */
6565 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
6567 gst_object_unref (pad2);
6571 GST_OBJECT_LOCK (tmptrans);
6572 has_matching_caps = (caps && tmptrans->codec_preferences &&
6573 !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
6574 GST_OBJECT_UNLOCK (tmptrans);
6575 /* Ignore transceivers with non-matching caps */
6576 if (!has_matching_caps)
6585 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
6586 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1));
6587 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
6589 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
6590 " for mline %u", trans, serial);
6592 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
6595 _update_transceiver_kind_from_caps (trans, caps);
6597 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
6598 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
6599 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
6600 webrtc->priv->pending_sink_transceivers =
6601 g_list_append (webrtc->priv->pending_sink_transceivers,
6602 gst_object_ref (pad));
6605 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
6606 wtrans->mline_locked = TRUE;
6607 trans->mline = serial;
6612 _add_pad (webrtc, pad);
6614 return GST_PAD (pad);
6622 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
6624 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6625 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
6627 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
6629 /* remove the transceiver from the pad so that subsequent code doesn't use
6630 * a possibly dead transceiver */
6632 if (webrtc_pad->trans)
6633 gst_object_unref (webrtc_pad->trans);
6634 webrtc_pad->trans = NULL;
6635 gst_caps_replace (&webrtc_pad->received_caps, NULL);
6638 _remove_pad (webrtc, webrtc_pad);
6641 _update_need_negotiation (webrtc);
6646 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
6651 /* Add an extra 50 ms for safety */
6652 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
6653 latency_ns *= GST_MSECOND;
6655 for (i = 0; i < webrtc->priv->transports->len; i++) {
6656 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
6657 GObject *storage = NULL;
6659 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
6662 g_object_set (storage, "size-time", latency_ns, NULL);
6664 g_object_unref (storage);
6669 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
6670 const GValue * value, GParamSpec * pspec)
6672 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6675 case PROP_STUN_SERVER:
6676 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
6677 g_value_get_string (value));
6679 case PROP_TURN_SERVER:
6680 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
6681 g_value_get_string (value));
6683 case PROP_BUNDLE_POLICY:
6684 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
6685 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
6687 webrtc->bundle_policy = g_value_get_enum (value);
6690 case PROP_ICE_TRANSPORT_POLICY:
6691 webrtc->ice_transport_policy = g_value_get_enum (value);
6692 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
6693 webrtc->ice_transport_policy ==
6694 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
6697 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
6698 webrtc->priv->jb_latency = g_value_get_uint (value);
6699 _update_rtpstorage_latency (webrtc);
6702 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
6708 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
6709 GValue * value, GParamSpec * pspec)
6711 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6715 case PROP_CONNECTION_STATE:
6716 g_value_set_enum (value, webrtc->peer_connection_state);
6718 case PROP_SIGNALING_STATE:
6719 g_value_set_enum (value, webrtc->signaling_state);
6721 case PROP_ICE_GATHERING_STATE:
6722 g_value_set_enum (value, webrtc->ice_gathering_state);
6724 case PROP_ICE_CONNECTION_STATE:
6725 g_value_set_enum (value, webrtc->ice_connection_state);
6727 case PROP_LOCAL_DESCRIPTION:
6728 if (webrtc->pending_local_description)
6729 g_value_set_boxed (value, webrtc->pending_local_description);
6730 else if (webrtc->current_local_description)
6731 g_value_set_boxed (value, webrtc->current_local_description);
6733 g_value_set_boxed (value, NULL);
6735 case PROP_CURRENT_LOCAL_DESCRIPTION:
6736 g_value_set_boxed (value, webrtc->current_local_description);
6738 case PROP_PENDING_LOCAL_DESCRIPTION:
6739 g_value_set_boxed (value, webrtc->pending_local_description);
6741 case PROP_REMOTE_DESCRIPTION:
6742 if (webrtc->pending_remote_description)
6743 g_value_set_boxed (value, webrtc->pending_remote_description);
6744 else if (webrtc->current_remote_description)
6745 g_value_set_boxed (value, webrtc->current_remote_description);
6747 g_value_set_boxed (value, NULL);
6749 case PROP_CURRENT_REMOTE_DESCRIPTION:
6750 g_value_set_boxed (value, webrtc->current_remote_description);
6752 case PROP_PENDING_REMOTE_DESCRIPTION:
6753 g_value_set_boxed (value, webrtc->pending_remote_description);
6755 case PROP_STUN_SERVER:
6756 g_value_take_string (value,
6757 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
6759 case PROP_TURN_SERVER:
6760 g_value_take_string (value,
6761 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
6763 case PROP_BUNDLE_POLICY:
6764 g_value_set_enum (value, webrtc->bundle_policy);
6766 case PROP_ICE_TRANSPORT_POLICY:
6767 g_value_set_enum (value, webrtc->ice_transport_policy);
6769 case PROP_ICE_AGENT:
6770 g_value_set_object (value, webrtc->priv->ice);
6773 g_value_set_uint (value, webrtc->priv->jb_latency);
6776 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
6783 gst_webrtc_bin_constructed (GObject * object)
6785 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6788 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
6789 webrtc->priv->ice = gst_webrtc_ice_new (name);
6791 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
6792 (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
6798 _free_pending_pad (GstPad * pad)
6800 gst_object_unref (pad);
6804 gst_webrtc_bin_dispose (GObject * object)
6806 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6808 if (webrtc->priv->ice)
6809 gst_object_unref (webrtc->priv->ice);
6810 webrtc->priv->ice = NULL;
6812 if (webrtc->priv->ice_stream_map)
6813 g_array_free (webrtc->priv->ice_stream_map, TRUE);
6814 webrtc->priv->ice_stream_map = NULL;
6816 g_clear_object (&webrtc->priv->sctp_transport);
6818 G_OBJECT_CLASS (parent_class)->dispose (object);
6822 gst_webrtc_bin_finalize (GObject * object)
6824 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6826 if (webrtc->priv->transports)
6827 g_ptr_array_free (webrtc->priv->transports, TRUE);
6828 webrtc->priv->transports = NULL;
6830 if (webrtc->priv->transceivers)
6831 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
6832 webrtc->priv->transceivers = NULL;
6834 if (webrtc->priv->data_channels)
6835 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
6836 webrtc->priv->data_channels = NULL;
6838 if (webrtc->priv->pending_data_channels)
6839 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
6840 webrtc->priv->pending_data_channels = NULL;
6842 if (webrtc->priv->pending_remote_ice_candidates)
6843 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
6844 webrtc->priv->pending_remote_ice_candidates = NULL;
6846 if (webrtc->priv->pending_local_ice_candidates)
6847 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
6848 webrtc->priv->pending_local_ice_candidates = NULL;
6850 if (webrtc->priv->pending_pads)
6851 g_list_free_full (webrtc->priv->pending_pads,
6852 (GDestroyNotify) _free_pending_pad);
6853 webrtc->priv->pending_pads = NULL;
6855 if (webrtc->priv->pending_sink_transceivers)
6856 g_list_free_full (webrtc->priv->pending_sink_transceivers,
6857 (GDestroyNotify) gst_object_unref);
6858 webrtc->priv->pending_sink_transceivers = NULL;
6860 if (webrtc->current_local_description)
6861 gst_webrtc_session_description_free (webrtc->current_local_description);
6862 webrtc->current_local_description = NULL;
6863 if (webrtc->pending_local_description)
6864 gst_webrtc_session_description_free (webrtc->pending_local_description);
6865 webrtc->pending_local_description = NULL;
6867 if (webrtc->current_remote_description)
6868 gst_webrtc_session_description_free (webrtc->current_remote_description);
6869 webrtc->current_remote_description = NULL;
6870 if (webrtc->pending_remote_description)
6871 gst_webrtc_session_description_free (webrtc->pending_remote_description);
6872 webrtc->pending_remote_description = NULL;
6874 if (webrtc->priv->last_generated_answer)
6875 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
6876 webrtc->priv->last_generated_answer = NULL;
6877 if (webrtc->priv->last_generated_offer)
6878 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
6879 webrtc->priv->last_generated_offer = NULL;
6881 g_mutex_clear (DC_GET_LOCK (webrtc));
6882 g_mutex_clear (ICE_GET_LOCK (webrtc));
6883 g_mutex_clear (PC_GET_LOCK (webrtc));
6884 g_cond_clear (PC_GET_COND (webrtc));
6886 G_OBJECT_CLASS (parent_class)->finalize (object);
6890 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
6892 GObjectClass *gobject_class = (GObjectClass *) klass;
6893 GstElementClass *element_class = (GstElementClass *) klass;
6895 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
6896 element_class->release_pad = gst_webrtc_bin_release_pad;
6897 element_class->change_state = gst_webrtc_bin_change_state;
6899 gst_element_class_add_static_pad_template_with_gtype (element_class,
6900 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
6901 gst_element_class_add_static_pad_template (element_class, &src_template);
6903 gst_element_class_set_metadata (element_class, "WebRTC Bin",
6904 "Filter/Network/WebRTC", "A bin for webrtc connections",
6905 "Matthew Waters <matthew@centricular.com>");
6907 gobject_class->constructed = gst_webrtc_bin_constructed;
6908 gobject_class->get_property = gst_webrtc_bin_get_property;
6909 gobject_class->set_property = gst_webrtc_bin_set_property;
6910 gobject_class->dispose = gst_webrtc_bin_dispose;
6911 gobject_class->finalize = gst_webrtc_bin_finalize;
6913 g_object_class_install_property (gobject_class,
6914 PROP_LOCAL_DESCRIPTION,
6915 g_param_spec_boxed ("local-description", "Local Description",
6916 "The local SDP description in use for this connection. "
6917 "Favours a pending description over the current description",
6918 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6919 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6921 g_object_class_install_property (gobject_class,
6922 PROP_CURRENT_LOCAL_DESCRIPTION,
6923 g_param_spec_boxed ("current-local-description",
6924 "Current Local Description",
6925 "The local description that was successfully negotiated the last time "
6926 "the connection transitioned into the stable state",
6927 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6928 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6930 g_object_class_install_property (gobject_class,
6931 PROP_PENDING_LOCAL_DESCRIPTION,
6932 g_param_spec_boxed ("pending-local-description",
6933 "Pending Local Description",
6934 "The local description that is in the process of being negotiated plus "
6935 "any local candidates that have been generated by the ICE Agent since the "
6936 "offer or answer was created",
6937 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6938 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6940 g_object_class_install_property (gobject_class,
6941 PROP_REMOTE_DESCRIPTION,
6942 g_param_spec_boxed ("remote-description", "Remote Description",
6943 "The remote SDP description to use for this connection. "
6944 "Favours a pending description over the current description",
6945 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6946 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6948 g_object_class_install_property (gobject_class,
6949 PROP_CURRENT_REMOTE_DESCRIPTION,
6950 g_param_spec_boxed ("current-remote-description",
6951 "Current Remote Description",
6952 "The last remote description that was successfully negotiated the last "
6953 "time the connection transitioned into the stable state plus any remote "
6954 "candidates that have been supplied via addIceCandidate() since the offer "
6955 "or answer was created",
6956 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6957 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6959 g_object_class_install_property (gobject_class,
6960 PROP_PENDING_REMOTE_DESCRIPTION,
6961 g_param_spec_boxed ("pending-remote-description",
6962 "Pending Remote Description",
6963 "The remote description that is in the process of being negotiated, "
6964 "complete with any remote candidates that have been supplied via "
6965 "addIceCandidate() since the offer or answer was created",
6966 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6967 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6969 g_object_class_install_property (gobject_class,
6971 g_param_spec_string ("stun-server", "STUN Server",
6972 "The STUN server of the form stun://hostname:port",
6973 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6975 g_object_class_install_property (gobject_class,
6977 g_param_spec_string ("turn-server", "TURN Server",
6978 "The TURN server of the form turn(s)://username:password@host:port. "
6979 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
6980 "if you wish to use multiple TURN servers",
6981 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6983 g_object_class_install_property (gobject_class,
6984 PROP_CONNECTION_STATE,
6985 g_param_spec_enum ("connection-state", "Connection State",
6986 "The overall connection state of this element",
6987 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
6988 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
6989 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6991 g_object_class_install_property (gobject_class,
6992 PROP_SIGNALING_STATE,
6993 g_param_spec_enum ("signaling-state", "Signaling State",
6994 "The signaling state of this element",
6995 GST_TYPE_WEBRTC_SIGNALING_STATE,
6996 GST_WEBRTC_SIGNALING_STATE_STABLE,
6997 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6999 g_object_class_install_property (gobject_class,
7000 PROP_ICE_CONNECTION_STATE,
7001 g_param_spec_enum ("ice-connection-state", "ICE connection state",
7002 "The collective connection state of all ICETransport's",
7003 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
7004 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
7005 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7007 g_object_class_install_property (gobject_class,
7008 PROP_ICE_GATHERING_STATE,
7009 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
7010 "The collective gathering state of all ICETransport's",
7011 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
7012 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
7013 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7015 g_object_class_install_property (gobject_class,
7017 g_param_spec_enum ("bundle-policy", "Bundle Policy",
7018 "The policy to apply for bundling",
7019 GST_TYPE_WEBRTC_BUNDLE_POLICY,
7020 GST_WEBRTC_BUNDLE_POLICY_NONE,
7021 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7023 g_object_class_install_property (gobject_class,
7024 PROP_ICE_TRANSPORT_POLICY,
7025 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
7026 "The policy to apply for ICE transport",
7027 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
7028 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
7029 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7031 g_object_class_install_property (gobject_class,
7033 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
7034 "The WebRTC ICE agent",
7035 GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7038 * GstWebRTCBin:latency:
7040 * Default duration to buffer in the jitterbuffers (in ms)
7045 g_object_class_install_property (gobject_class,
7047 g_param_spec_uint ("latency", "Latency",
7048 "Default duration to buffer in the jitterbuffers (in ms)",
7049 0, G_MAXUINT, 200, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7052 * GstWebRTCBin::create-offer:
7053 * @object: the #webrtcbin
7054 * @options: (nullable): create-offer options
7055 * @promise: a #GstPromise which will contain the offer
7057 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
7058 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
7059 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7060 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
7061 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7064 * GstWebRTCBin::create-answer:
7065 * @object: the #webrtcbin
7066 * @options: (nullable): create-answer options
7067 * @promise: a #GstPromise which will contain the answer
7069 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
7070 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
7071 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7072 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
7073 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7076 * GstWebRTCBin::set-local-description:
7077 * @object: the #GstWebRTCBin
7078 * @desc: a #GstWebRTCSessionDescription description
7079 * @promise: (nullable): a #GstPromise to be notified when it's set
7081 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
7082 g_signal_new_class_handler ("set-local-description",
7083 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7084 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
7085 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7088 * GstWebRTCBin::set-remote-description:
7089 * @object: the #GstWebRTCBin
7090 * @desc: a #GstWebRTCSessionDescription description
7091 * @promise: (nullable): a #GstPromise to be notified when it's set
7093 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
7094 g_signal_new_class_handler ("set-remote-description",
7095 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7096 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
7097 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7100 * GstWebRTCBin::add-ice-candidate:
7101 * @object: the #webrtcbin
7102 * @mline_index: the index of the media description in the SDP
7103 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
7106 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
7107 g_signal_new_class_handler ("add-ice-candidate",
7108 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7109 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
7110 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7113 * GstWebRTCBin::get-stats:
7114 * @object: the #webrtcbin
7115 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
7116 * @promise: a #GstPromise for the result
7118 * The @promise will contain the result of retrieving the session statistics.
7119 * The structure will be named 'application/x-webrtc-stats and contain the
7120 * following based on the webrtc-stats spec available from
7121 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
7122 * and is constantly changing these statistics may be changed to fit with
7125 * Each field key is a unique identifier for each RTCStats
7126 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
7127 * GstStructure) in the RTCStatsReport
7128 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
7129 * field in the RTCStats subclass is outlined below.
7131 * Each statistics structure contains the following values as defined by
7132 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
7134 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
7135 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
7136 * "id" G_TYPE_STRING unique identifier
7138 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
7140 * "payload-type" G_TYPE_UINT the rtp payload number in use
7141 * "clock-rate" G_TYPE_UINT the rtp clock-rate
7143 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
7145 * "ssrc" G_TYPE_STRING the rtp sequence src in use
7146 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
7147 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
7148 * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
7149 * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
7150 * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
7152 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
7154 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
7155 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
7156 * "packets-lost" G_TYPE_UINT number of packets lost
7157 * "jitter" G_TYPE_DOUBLE packet jitter measured in secondss
7159 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
7161 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
7163 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
7165 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
7166 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
7168 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
7170 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7171 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7173 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
7175 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
7177 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
7179 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
7182 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
7183 g_signal_new_class_handler ("get-stats",
7184 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7185 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
7186 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
7189 * GstWebRTCBin::on-negotiation-needed:
7190 * @object: the #webrtcbin
7192 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
7193 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
7194 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
7197 * GstWebRTCBin::on-ice-candidate:
7198 * @object: the #webrtcbin
7199 * @mline_index: the index of the media description in the SDP
7200 * @candidate: the ICE candidate
7202 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
7203 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
7204 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7205 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7208 * GstWebRTCBin::on-new-transceiver:
7209 * @object: the #webrtcbin
7210 * @candidate: the new #GstWebRTCRTPTransceiver
7212 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
7213 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
7214 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7215 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
7218 * GstWebRTCBin::on-data-channel:
7219 * @object: the #GstWebRTCBin
7220 * @candidate: the new `GstWebRTCDataChannel`
7222 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
7223 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
7224 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7225 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
7228 * GstWebRTCBin::add-transceiver:
7229 * @object: the #webrtcbin
7230 * @direction: the direction of the new transceiver
7231 * @caps: (allow none): the codec preferences for this transceiver
7233 * Returns: the new #GstWebRTCRTPTransceiver
7235 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
7236 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
7237 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7238 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
7239 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
7240 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
7243 * GstWebRTCBin::get-transceivers:
7244 * @object: the #webrtcbin
7246 * Returns: a #GArray of #GstWebRTCRTPTransceivers
7248 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
7249 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
7250 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7251 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
7255 * GstWebRTCBin::get-transceiver:
7256 * @object: the #GstWebRTCBin
7257 * @idx: The index of the transceiver
7259 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
7262 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
7263 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
7264 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7265 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
7266 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
7269 * GstWebRTCBin::add-turn-server:
7270 * @object: the #GstWebRTCBin
7271 * @uri: The uri of the server of the form turn(s)://username:password@host:port
7273 * Add a turn server to obtain ICE candidates from
7275 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
7276 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
7277 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7278 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
7279 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
7282 * GstWebRTCBin::create-data-channel:
7283 * @object: the #GstWebRTCBin
7284 * @label: the label for the data channel
7285 * @options: a #GstStructure of options for creating the data channel
7287 * The options dictionary is the same format as the RTCDataChannelInit
7288 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
7289 * and reproduced below
7291 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
7292 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
7293 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
7294 * protocol G_TYPE_STRING The subprotocol used by this channel
7295 * 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.
7296 * id G_TYPE_INT Override the default identifier selection of this channel
7297 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
7299 * Returns: (transfer full): a new data channel object
7301 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
7302 g_signal_new_class_handler ("create-data-channel",
7303 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7304 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
7305 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
7307 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
7308 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
7312 _unparent_and_unref (GObject * object)
7314 GstObject *obj = GST_OBJECT (object);
7316 GST_OBJECT_PARENT (obj) = NULL;
7318 gst_object_unref (obj);
7322 _transport_free (GObject * object)
7324 TransportStream *stream = (TransportStream *) object;
7325 GstWebRTCBin *webrtc;
7327 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
7329 if (stream->transport) {
7330 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
7331 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
7334 gst_object_unref (object);
7338 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
7340 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
7341 g_mutex_init (PC_GET_LOCK (webrtc));
7342 g_cond_init (PC_GET_COND (webrtc));
7344 g_mutex_init (ICE_GET_LOCK (webrtc));
7345 g_mutex_init (DC_GET_LOCK (webrtc));
7347 webrtc->rtpbin = _create_rtpbin (webrtc);
7348 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
7350 webrtc->priv->transceivers =
7351 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
7352 webrtc->priv->transports =
7353 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
7355 webrtc->priv->data_channels =
7356 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7358 webrtc->priv->pending_data_channels =
7359 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7361 webrtc->priv->ice_stream_map =
7362 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
7363 webrtc->priv->pending_remote_ice_candidates =
7364 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7365 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
7366 (GDestroyNotify) _clear_ice_candidate_item);
7368 webrtc->priv->pending_local_ice_candidates =
7369 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7370 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
7371 (GDestroyNotify) _clear_ice_candidate_item);
7373 /* we start off closed until we move to READY */
7374 webrtc->priv->is_closed = TRUE;