2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31 #include "webrtcdatachannel.h"
32 #include "webrtcsctptransport.h"
34 #include "gst/webrtc/webrtc-priv.h"
36 #include <gst/rtp/rtp.h>
42 #define RANDOM_SESSION_ID \
43 ((((((guint64) g_random_int()) << 32) | \
44 (guint64) g_random_int ())) & \
45 G_GUINT64_CONSTANT (0x7fffffffffffffff))
47 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
48 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
49 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
51 #define PC_GET_COND(w) (&w->priv->pc_cond)
52 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
53 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
54 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
56 #define ICE_GET_LOCK(w) (&w->priv->ice_lock)
57 #define ICE_LOCK(w) (g_mutex_lock (ICE_GET_LOCK(w)))
58 #define ICE_UNLOCK(w) (g_mutex_unlock (ICE_GET_LOCK(w)))
60 #define DC_GET_LOCK(w) (&w->priv->dc_lock)
61 #define DC_LOCK(w) (g_mutex_lock (DC_GET_LOCK(w)))
62 #define DC_UNLOCK(w) (g_mutex_unlock (DC_GET_LOCK(w)))
64 /* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */
65 #define RTPSTORAGE_EXTRA_TIME (50)
67 #define DEFAULT_JB_LATENCY 200
70 * SECTION: element-webrtcbin
73 * This webrtcbin implements the majority of the W3's peerconnection API and
74 * implementation guide where possible. Generating offers, answers and setting
75 * local and remote SDP's are all supported. Both media descriptions and
76 * descriptions involving data channels are supported.
78 * Each input/output pad is equivalent to a Track in W3 parlance which are
79 * added/removed from the bin. The number of requested sink pads is the number
80 * of streams that will be sent to the receiver and will be associated with a
81 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
83 * On the receiving side, RTPTransceiver's are created in response to setting
84 * a remote description. Output pads for the receiving streams in the set
85 * description are also created when data is received.
87 * A TransportStream is created when needed in order to transport the data over
88 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
89 * on the negotiated SDP's between the peers based on the bundle and rtcp
90 * configuration. Some cases are outlined below for a simple single
91 * audio/video/data session:
93 * - max-bundle uses a single transport for all
94 * media/data transported. Renegotiation involves adding/removing the
95 * necessary streams to the existing transports.
96 * - max-compat involves two TransportStream per media stream
97 * to transport the rtp and the rtcp packets and a single TransportStream for
98 * all data channels. Each stream change involves modifying the associated
99 * TransportStream/s as necessary.
104 * assert sending payload type matches the stream
105 * reconfiguration (of anything)
107 * balanced bundle policy
108 * setting custom DTLS certificates
110 * separate session id's from mlineindex properly
111 * how to deal with replacing a input/output track/stream
114 static void _update_need_negotiation (GstWebRTCBin * webrtc);
115 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
116 GstWebRTCBinPad * pad);
119 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
120 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
122 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
125 GST_STATIC_CAPS ("application/x-rtp"));
127 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
130 GST_STATIC_CAPS ("application/x-rtp"));
134 PROP_PAD_TRANSCEIVER = 1,
138 _have_nice_elements (GstWebRTCBin * webrtc)
140 GstPluginFeature *feature;
142 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
144 gst_object_unref (feature);
146 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
147 ("%s", "libnice elements are not available"));
151 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
153 gst_object_unref (feature);
155 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
156 ("%s", "libnice elements are not available"));
164 _have_sctp_elements (GstWebRTCBin * webrtc)
166 GstPluginFeature *feature;
168 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
170 gst_object_unref (feature);
172 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
173 ("%s", "sctp elements are not available"));
177 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
179 gst_object_unref (feature);
181 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
182 ("%s", "sctp elements are not available"));
190 _have_dtls_elements (GstWebRTCBin * webrtc)
192 GstPluginFeature *feature;
194 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
196 gst_object_unref (feature);
198 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
199 ("%s", "dtls elements are not available"));
203 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
205 gst_object_unref (feature);
207 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
208 ("%s", "dtls elements are not available"));
215 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
218 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
219 GValue * value, GParamSpec * pspec)
221 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
224 case PROP_PAD_TRANSCEIVER:
225 g_value_set_object (value, pad->trans);
228 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234 gst_webrtc_bin_pad_finalize (GObject * object)
236 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
239 gst_object_unref (pad->trans);
242 if (pad->received_caps)
243 gst_caps_unref (pad->received_caps);
244 pad->received_caps = NULL;
246 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
250 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
252 GObjectClass *gobject_class = (GObjectClass *) klass;
254 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
255 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
257 g_object_class_install_property (gobject_class,
258 PROP_PAD_TRANSCEIVER,
259 g_param_spec_object ("transceiver", "Transceiver",
260 "Transceiver associated with this pad",
261 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
262 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
266 gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
268 if (wpad->received_caps) {
269 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
270 GstPad *pad = GST_PAD (wpad);
272 gst_event_take (&trans->ssrc_event,
273 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
274 gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
275 trans->current_ssrc, NULL)));
277 gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
282 _get_pending_sink_transceiver (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
286 for (ret = webrtc->priv->pending_sink_transceivers; ret; ret = ret->next) {
287 if (ret->data == pad)
295 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
297 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
298 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
299 gboolean check_negotiation = FALSE;
301 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
304 gst_event_parse_caps (event, &caps);
305 check_negotiation = (!wpad->received_caps
306 || !gst_caps_is_equal (wpad->received_caps, caps));
307 gst_caps_replace (&wpad->received_caps, caps);
309 GST_DEBUG_OBJECT (parent,
310 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
311 GST_PTR_FORMAT, pad, check_negotiation, caps);
313 if (check_negotiation) {
314 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
315 const GstStructure *s;
317 s = gst_caps_get_structure (caps, 0);
318 gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
319 gst_webrtc_bin_pad_update_ssrc_event (wpad);
322 /* A remote description might have been set while the pad hadn't
323 * yet received caps, delaying the connection of the input stream
327 GST_OBJECT_LOCK (wpad->trans);
328 if (wpad->trans->current_direction ==
329 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY
330 || wpad->trans->current_direction ==
331 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
332 GList *pending = _get_pending_sink_transceiver (webrtc, wpad);
335 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
336 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
337 wpad->trans, wpad->received_caps);
338 _connect_input_stream (webrtc, wpad);
339 gst_pad_remove_probe (GST_PAD (pad), wpad->block_id);
341 gst_object_unref (pending->data);
342 webrtc->priv->pending_sink_transceivers =
343 g_list_delete_link (webrtc->priv->pending_sink_transceivers,
347 GST_OBJECT_UNLOCK (wpad->trans);
350 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
351 check_negotiation = TRUE;
354 if (check_negotiation) {
356 _update_need_negotiation (webrtc);
360 return gst_pad_event_default (pad, parent, event);
364 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
366 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
367 gboolean ret = FALSE;
369 switch (GST_QUERY_TYPE (query)) {
370 case GST_QUERY_ACCEPT_CAPS:
371 GST_OBJECT_LOCK (wpad->trans);
372 if (wpad->trans->codec_preferences) {
375 gst_query_parse_accept_caps (query, &caps);
377 gst_query_set_accept_caps_result (query,
378 gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
381 GST_OBJECT_UNLOCK (wpad->trans);
386 GstCaps *codec_preferences = NULL;
388 GST_OBJECT_LOCK (wpad->trans);
389 if (wpad->trans->codec_preferences)
390 codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
391 GST_OBJECT_UNLOCK (wpad->trans);
393 if (codec_preferences) {
394 GstCaps *filter = NULL;
395 GstCaps *filter_prefs = NULL;
398 gst_query_parse_caps (query, &filter);
401 filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
402 GST_CAPS_INTERSECT_FIRST);
403 gst_caps_unref (codec_preferences);
405 filter_prefs = codec_preferences;
408 target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
412 result = gst_pad_query_caps (target, filter_prefs);
413 gst_query_set_caps_result (query, result);
414 gst_caps_unref (result);
416 gst_object_unref (target);
418 gst_query_set_caps_result (query, filter_prefs);
421 gst_caps_unref (filter_prefs);
433 return gst_pad_query_default (pad, parent, query);
438 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
442 static GstPadProbeReturn
443 webrtc_bin_pad_buffer_cb (GstPad * pad, GstPadProbeInfo * info,
446 GstWebRTCBinPad *wpad;
448 GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT;
450 if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
451 buf = GST_PAD_PROBE_INFO_BUFFER (info);
455 list = GST_PAD_PROBE_INFO_BUFFER_LIST (info);
456 buf = gst_buffer_list_get (list, 0);
460 return GST_PAD_PROBE_OK;
462 if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf))
463 return GST_PAD_PROBE_OK;
465 wpad = GST_WEBRTC_BIN_PAD (pad);
466 wpad->last_ssrc = gst_rtp_buffer_get_ssrc (&rtpbuf);
468 gst_rtp_buffer_unmap (&rtpbuf);
470 return GST_PAD_PROBE_OK;
473 static GstWebRTCBinPad *
474 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
476 GstWebRTCBinPad *pad;
477 GstPadTemplate *template;
479 if (direction == GST_PAD_SINK)
480 template = gst_static_pad_template_get (&sink_template);
481 else if (direction == GST_PAD_SRC)
482 template = gst_static_pad_template_get (&src_template);
484 g_assert_not_reached ();
487 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
488 direction, "template", template, NULL);
489 gst_object_unref (template);
491 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
492 gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
494 gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BUFFER |
495 GST_PAD_PROBE_TYPE_BUFFER_LIST, webrtc_bin_pad_buffer_cb, NULL, NULL);
497 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
498 direction == GST_PAD_SRC ? "src" : "sink");
502 #define gst_webrtc_bin_parent_class parent_class
503 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
504 G_ADD_PRIVATE (GstWebRTCBin)
505 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
506 "webrtcbin element"););
512 CREATE_ANSWER_SIGNAL,
513 SET_LOCAL_DESCRIPTION_SIGNAL,
514 SET_REMOTE_DESCRIPTION_SIGNAL,
515 ADD_ICE_CANDIDATE_SIGNAL,
516 ON_NEGOTIATION_NEEDED_SIGNAL,
517 ON_ICE_CANDIDATE_SIGNAL,
518 ON_NEW_TRANSCEIVER_SIGNAL,
520 ADD_TRANSCEIVER_SIGNAL,
521 GET_TRANSCEIVER_SIGNAL,
522 GET_TRANSCEIVERS_SIGNAL,
523 ADD_TURN_SERVER_SIGNAL,
524 CREATE_DATA_CHANNEL_SIGNAL,
525 ON_DATA_CHANNEL_SIGNAL,
532 PROP_CONNECTION_STATE,
533 PROP_SIGNALING_STATE,
534 PROP_ICE_GATHERING_STATE,
535 PROP_ICE_CONNECTION_STATE,
536 PROP_LOCAL_DESCRIPTION,
537 PROP_CURRENT_LOCAL_DESCRIPTION,
538 PROP_PENDING_LOCAL_DESCRIPTION,
539 PROP_REMOTE_DESCRIPTION,
540 PROP_CURRENT_REMOTE_DESCRIPTION,
541 PROP_PENDING_REMOTE_DESCRIPTION,
545 PROP_ICE_TRANSPORT_POLICY,
551 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
556 GstWebRTCICEStream *stream;
559 /* FIXME: locking? */
561 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
565 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
566 IceStreamItem *item =
567 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
569 if (item->session_id == session_id) {
570 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
571 "session %u", item->stream, session_id);
576 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
582 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
583 GstWebRTCICEStream * stream)
585 IceStreamItem item = { session_id, stream };
587 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
588 "session %u", stream, session_id);
589 g_array_append_val (webrtc->priv->ice_stream_map, item);
592 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
595 static GstWebRTCRTPTransceiver *
596 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
597 FindTransceiverFunc func)
601 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
602 GstWebRTCRTPTransceiver *transceiver =
603 g_ptr_array_index (webrtc->priv->transceivers, i);
605 if (func (transceiver, data))
613 transceiver_match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
615 return g_strcmp0 (trans->mid, mid) == 0;
619 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
624 return trans->mline == *mline;
627 static GstWebRTCRTPTransceiver *
628 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
630 GstWebRTCRTPTransceiver *trans;
632 trans = _find_transceiver (webrtc, &mlineindex,
633 (FindTransceiverFunc) transceiver_match_for_mline);
635 GST_TRACE_OBJECT (webrtc,
636 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
642 static GstWebRTCRTPTransceiver *
643 _find_transceiver_for_mid (GstWebRTCBin * webrtc, const char *mid)
645 GstWebRTCRTPTransceiver *trans;
647 trans = _find_transceiver (webrtc, mid,
648 (FindTransceiverFunc) transceiver_match_for_mid);
650 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT " for "
651 "mid %s", trans, mid);
656 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
659 static TransportStream *
660 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
661 FindTransportFunc func)
665 for (i = 0; i < webrtc->priv->transports->len; i++) {
666 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
668 if (func (stream, data))
676 match_stream_for_session (TransportStream * trans, guint * session)
678 return trans->session_id == *session;
681 static TransportStream *
682 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
684 TransportStream *stream;
686 stream = _find_transport (webrtc, &session_id,
687 (FindTransportFunc) match_stream_for_session);
689 GST_TRACE_OBJECT (webrtc,
690 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
695 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
697 static GstWebRTCBinPad *
698 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
700 GstElement *element = GST_ELEMENT (webrtc);
703 GST_OBJECT_LOCK (webrtc);
705 for (; l; l = g_list_next (l)) {
706 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
708 if (func (l->data, data)) {
709 gst_object_ref (l->data);
710 GST_OBJECT_UNLOCK (webrtc);
715 l = webrtc->priv->pending_pads;
716 for (; l; l = g_list_next (l)) {
717 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
719 if (func (l->data, data)) {
720 gst_object_ref (l->data);
721 GST_OBJECT_UNLOCK (webrtc);
725 GST_OBJECT_UNLOCK (webrtc);
730 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
733 static WebRTCDataChannel *
734 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
735 FindDataChannelFunc func)
739 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
740 WebRTCDataChannel *channel =
741 g_ptr_array_index (webrtc->priv->data_channels, i);
743 if (func (channel, data))
751 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
753 return channel->parent.id == *id;
756 /* always called with dc_lock held */
757 static WebRTCDataChannel *
758 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
760 WebRTCDataChannel *channel;
762 channel = _find_data_channel (webrtc, &id,
763 (FindDataChannelFunc) data_channel_match_for_id);
765 GST_TRACE_OBJECT (webrtc,
766 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
772 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
774 GST_OBJECT_LOCK (webrtc);
775 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
776 GST_OBJECT_UNLOCK (webrtc);
780 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
782 gboolean ret = FALSE;
785 GST_OBJECT_LOCK (webrtc);
786 l = g_list_find (webrtc->priv->pending_pads, pad);
788 webrtc->priv->pending_pads =
789 g_list_remove_link (webrtc->priv->pending_pads, l);
793 GST_OBJECT_UNLOCK (webrtc);
799 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
801 _remove_pending_pad (webrtc, pad);
803 if (webrtc->priv->running)
804 gst_pad_set_active (GST_PAD (pad), TRUE);
805 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
809 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
811 _remove_pending_pad (webrtc, pad);
813 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
818 GstPadDirection direction;
823 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
825 return GST_PAD_DIRECTION (pad) == match->direction
826 && pad->trans->mline == match->mline;
829 static GstWebRTCBinPad *
830 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
833 MLineMatch m = { direction, mline };
835 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
840 GstPadDirection direction;
841 GstWebRTCRTPTransceiver *trans;
845 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
847 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
850 static GstWebRTCBinPad *
851 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
852 GstWebRTCRTPTransceiver * trans)
854 TransMatch m = { direction, trans };
856 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
861 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
863 return pad->ssrc == *ssrc;
867 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
874 _unlock_pc_thread (GMutex * lock)
876 g_mutex_unlock (lock);
877 return G_SOURCE_REMOVE;
881 _gst_pc_thread (GstWebRTCBin * webrtc)
884 webrtc->priv->main_context = g_main_context_new ();
885 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
887 PC_COND_BROADCAST (webrtc);
888 g_main_context_invoke (webrtc->priv->main_context,
889 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
891 /* Having the thread be the thread default GMainContext will break the
892 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
894 g_main_loop_run (webrtc->priv->loop);
896 GST_OBJECT_LOCK (webrtc);
897 g_main_context_unref (webrtc->priv->main_context);
898 webrtc->priv->main_context = NULL;
899 GST_OBJECT_UNLOCK (webrtc);
902 g_main_loop_unref (webrtc->priv->loop);
903 webrtc->priv->loop = NULL;
904 PC_COND_BROADCAST (webrtc);
911 _start_thread (GstWebRTCBin * webrtc)
916 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
917 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
921 while (!webrtc->priv->loop)
922 PC_COND_WAIT (webrtc);
923 webrtc->priv->is_closed = FALSE;
928 _stop_thread (GstWebRTCBin * webrtc)
930 GST_OBJECT_LOCK (webrtc);
931 webrtc->priv->is_closed = TRUE;
932 GST_OBJECT_UNLOCK (webrtc);
935 g_main_loop_quit (webrtc->priv->loop);
936 while (webrtc->priv->loop)
937 PC_COND_WAIT (webrtc);
940 g_thread_unref (webrtc->priv->thread);
944 _execute_op (GstWebRTCBinTask * op)
948 PC_LOCK (op->webrtc);
949 if (op->webrtc->priv->is_closed) {
950 PC_UNLOCK (op->webrtc);
954 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
955 "webrtcbin is closed. aborting execution.");
956 GstStructure *s = gst_structure_new ("application/x-gst-promise",
957 "error", G_TYPE_ERROR, error, NULL);
959 gst_promise_reply (op->promise, s);
961 g_clear_error (&error);
963 GST_DEBUG_OBJECT (op->webrtc,
964 "Peerconnection is closed, aborting execution");
968 s = op->op (op->webrtc, op->data);
970 PC_UNLOCK (op->webrtc);
973 gst_promise_reply (op->promise, s);
975 gst_structure_free (s);
978 return G_SOURCE_REMOVE;
982 _free_op (GstWebRTCBinTask * op)
985 op->notify (op->data);
987 gst_promise_unref (op->promise);
992 * @promise is for correctly signalling the failure case to the caller when
993 * the user supplies it. Without passing it in, the promise would never
994 * be replied to in the case that @webrtc becomes closed between the idle
995 * source addition and the the execution of the idle source.
998 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
999 gpointer data, GDestroyNotify notify, GstPromise * promise)
1001 GstWebRTCBinTask *op;
1005 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
1007 GST_OBJECT_LOCK (webrtc);
1008 if (webrtc->priv->is_closed) {
1009 GST_OBJECT_UNLOCK (webrtc);
1010 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
1015 ctx = g_main_context_ref (webrtc->priv->main_context);
1016 GST_OBJECT_UNLOCK (webrtc);
1018 op = g_new0 (GstWebRTCBinTask, 1);
1019 op->webrtc = webrtc;
1022 op->notify = notify;
1024 op->promise = gst_promise_ref (promise);
1026 source = g_idle_source_new ();
1027 g_source_set_priority (source, G_PRIORITY_DEFAULT);
1028 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
1029 (GDestroyNotify) _free_op);
1030 g_source_attach (source, ctx);
1031 g_source_unref (source);
1032 g_main_context_unref (ctx);
1037 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
1038 static GstWebRTCICEConnectionState
1039 _collate_ice_connection_states (GstWebRTCBin * webrtc)
1041 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
1042 GstWebRTCICEConnectionState any_state = 0;
1043 gboolean all_new_or_closed = TRUE;
1044 gboolean all_completed_or_closed = TRUE;
1045 gboolean all_connected_completed_or_closed = TRUE;
1048 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1049 GstWebRTCRTPTransceiver *rtp_trans =
1050 g_ptr_array_index (webrtc->priv->transceivers, i);
1051 GstWebRTCICETransport *transport;
1052 GstWebRTCICEConnectionState ice_state;
1054 if (rtp_trans->stopped) {
1055 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1059 if (!rtp_trans->mid) {
1060 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1064 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
1066 /* get transport state */
1067 g_object_get (transport, "state", &ice_state, NULL);
1068 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
1070 any_state |= (1 << ice_state);
1072 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
1073 all_new_or_closed = FALSE;
1074 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
1075 all_completed_or_closed = FALSE;
1076 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
1077 && ice_state != STATE (CLOSED))
1078 all_connected_completed_or_closed = FALSE;
1081 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
1083 if (webrtc->priv->is_closed) {
1084 GST_TRACE_OBJECT (webrtc, "returning closed");
1085 return STATE (CLOSED);
1087 /* Any of the RTCIceTransports are in the failed state. */
1088 if (any_state & (1 << STATE (FAILED))) {
1089 GST_TRACE_OBJECT (webrtc, "returning failed");
1090 return STATE (FAILED);
1092 /* Any of the RTCIceTransports are in the disconnected state. */
1093 if (any_state & (1 << STATE (DISCONNECTED))) {
1094 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1095 return STATE (DISCONNECTED);
1097 /* All of the RTCIceTransports are in the new or closed state, or there are
1099 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
1100 GST_TRACE_OBJECT (webrtc, "returning new");
1103 /* Any of the RTCIceTransports are in the checking or new state. */
1104 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
1105 GST_TRACE_OBJECT (webrtc, "returning checking");
1106 return STATE (CHECKING);
1108 /* All RTCIceTransports are in the completed or closed state. */
1109 if (all_completed_or_closed) {
1110 GST_TRACE_OBJECT (webrtc, "returning completed");
1111 return STATE (COMPLETED);
1113 /* All RTCIceTransports are in the connected, completed or closed state. */
1114 if (all_connected_completed_or_closed) {
1115 GST_TRACE_OBJECT (webrtc, "returning connected");
1116 return STATE (CONNECTED);
1119 GST_FIXME ("unspecified situation, returning old state");
1120 return webrtc->ice_connection_state;
1124 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1125 static GstWebRTCICEGatheringState
1126 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1128 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1129 GstWebRTCICEGatheringState any_state = 0;
1130 GstWebRTCICEGatheringState ice_state;
1131 GstWebRTCDTLSTransport *dtls_transport;
1132 GstWebRTCICETransport *transport;
1133 gboolean all_completed = webrtc->priv->transceivers->len > 0 ||
1134 webrtc->priv->data_channel_transport;
1137 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1138 GstWebRTCRTPTransceiver *rtp_trans =
1139 g_ptr_array_index (webrtc->priv->transceivers, i);
1140 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1141 TransportStream *stream = trans->stream;
1143 if (rtp_trans->stopped || stream == NULL) {
1144 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1149 /* We only have a mid in the transceiver after we got the SDP answer,
1150 * which is usually long after gathering has finished */
1151 if (!rtp_trans->mid) {
1152 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1155 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1156 if (dtls_transport == NULL) {
1157 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1161 transport = dtls_transport->transport;
1163 /* get gathering state */
1164 g_object_get (transport, "gathering-state", &ice_state, NULL);
1165 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1167 any_state |= (1 << ice_state);
1168 if (ice_state != STATE (COMPLETE))
1169 all_completed = FALSE;
1172 /* check data channel transport gathering state */
1173 if (all_completed && webrtc->priv->data_channel_transport) {
1174 if ((dtls_transport = webrtc->priv->data_channel_transport->transport)) {
1175 transport = dtls_transport->transport;
1176 g_object_get (transport, "gathering-state", &ice_state, NULL);
1177 GST_TRACE_OBJECT (webrtc,
1178 "data channel transport %p gathering state: 0x%x", dtls_transport,
1180 any_state |= (1 << ice_state);
1181 if (ice_state != STATE (COMPLETE))
1182 all_completed = FALSE;
1186 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1188 /* Any of the RTCIceTransport s are in the gathering state. */
1189 if (any_state & (1 << STATE (GATHERING))) {
1190 GST_TRACE_OBJECT (webrtc, "returning gathering");
1191 return STATE (GATHERING);
1193 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1194 * the completed gathering state. */
1195 if (all_completed) {
1196 GST_TRACE_OBJECT (webrtc, "returning complete");
1197 return STATE (COMPLETE);
1200 /* Any of the RTCIceTransport s are in the new gathering state and none
1201 * of the transports are in the gathering state, or there are no transports. */
1202 GST_TRACE_OBJECT (webrtc, "returning new");
1207 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1208 static GstWebRTCPeerConnectionState
1209 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1211 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1212 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1213 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1214 GstWebRTCICEConnectionState any_ice_state = 0;
1215 GstWebRTCDTLSTransportState any_dtls_state = 0;
1216 gboolean ice_all_new_or_closed = TRUE;
1217 gboolean dtls_all_new_or_closed = TRUE;
1218 gboolean ice_all_new_connecting_or_checking = TRUE;
1219 gboolean dtls_all_new_connecting_or_checking = TRUE;
1220 gboolean ice_all_connected_completed_or_closed = TRUE;
1221 gboolean dtls_all_connected_completed_or_closed = TRUE;
1224 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1225 GstWebRTCRTPTransceiver *rtp_trans =
1226 g_ptr_array_index (webrtc->priv->transceivers, i);
1227 GstWebRTCDTLSTransport *transport;
1228 GstWebRTCICEConnectionState ice_state;
1229 GstWebRTCDTLSTransportState dtls_state;
1231 if (rtp_trans->stopped) {
1232 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1235 if (!rtp_trans->mid) {
1236 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1240 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1242 /* get transport state */
1243 g_object_get (transport, "state", &dtls_state, NULL);
1244 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1246 any_dtls_state |= (1 << dtls_state);
1248 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1249 dtls_all_new_or_closed = FALSE;
1250 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1251 dtls_all_new_connecting_or_checking = FALSE;
1252 if (dtls_state != DTLS_STATE (CONNECTED)
1253 && dtls_state != DTLS_STATE (CLOSED))
1254 dtls_all_connected_completed_or_closed = FALSE;
1256 g_object_get (transport->transport, "state", &ice_state, NULL);
1257 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1259 any_ice_state |= (1 << ice_state);
1261 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1262 ice_all_new_or_closed = FALSE;
1263 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1264 ice_all_new_connecting_or_checking = FALSE;
1265 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1266 && ice_state != ICE_STATE (CLOSED))
1267 ice_all_connected_completed_or_closed = FALSE;
1270 // also check data channel transport state
1271 if (webrtc->priv->data_channel_transport) {
1272 GstWebRTCDTLSTransport *transport =
1273 webrtc->priv->data_channel_transport->transport;
1274 GstWebRTCICEConnectionState ice_state;
1275 GstWebRTCDTLSTransportState dtls_state;
1277 g_object_get (transport, "state", &dtls_state, NULL);
1278 GST_TRACE_OBJECT (webrtc, "data channel transport DTLS state: 0x%x",
1280 any_dtls_state |= (1 << dtls_state);
1282 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1283 dtls_all_new_or_closed = FALSE;
1284 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1285 dtls_all_new_connecting_or_checking = FALSE;
1286 if (dtls_state != DTLS_STATE (CONNECTED)
1287 && dtls_state != DTLS_STATE (CLOSED))
1288 dtls_all_connected_completed_or_closed = FALSE;
1290 g_object_get (transport->transport, "state", &ice_state, NULL);
1291 GST_TRACE_OBJECT (webrtc, "data channel transport ICE state: 0x%x",
1293 any_ice_state |= (1 << ice_state);
1295 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1296 ice_all_new_or_closed = FALSE;
1297 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1298 ice_all_new_connecting_or_checking = FALSE;
1299 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1300 && ice_state != ICE_STATE (CLOSED))
1301 ice_all_connected_completed_or_closed = FALSE;
1304 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1305 "state: 0x%x", any_ice_state, any_dtls_state);
1307 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1308 if (webrtc->priv->is_closed) {
1309 GST_TRACE_OBJECT (webrtc, "returning closed");
1310 return STATE (CLOSED);
1313 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1314 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1315 GST_TRACE_OBJECT (webrtc, "returning failed");
1316 return STATE (FAILED);
1318 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1319 GST_TRACE_OBJECT (webrtc, "returning failed");
1320 return STATE (FAILED);
1323 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1325 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1326 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1327 return STATE (DISCONNECTED);
1330 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1331 * state, or there are no transports. */
1332 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1333 || webrtc->priv->transports->len == 0) {
1334 GST_TRACE_OBJECT (webrtc, "returning new");
1338 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1339 * or checking state. */
1340 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1341 GST_TRACE_OBJECT (webrtc, "returning connecting");
1342 return STATE (CONNECTING);
1345 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1346 * completed or closed state. */
1347 if (dtls_all_connected_completed_or_closed
1348 && ice_all_connected_completed_or_closed) {
1349 GST_TRACE_OBJECT (webrtc, "returning connected");
1350 return STATE (CONNECTED);
1353 /* FIXME: Unspecified state that happens for us */
1354 if ((dtls_all_new_connecting_or_checking
1355 || dtls_all_connected_completed_or_closed)
1356 && (ice_all_new_connecting_or_checking
1357 || ice_all_connected_completed_or_closed)) {
1358 GST_TRACE_OBJECT (webrtc, "returning connecting");
1359 return STATE (CONNECTING);
1362 GST_FIXME_OBJECT (webrtc,
1363 "Undefined situation detected, returning old state");
1364 return webrtc->peer_connection_state;
1370 static GstStructure *
1371 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1373 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1374 GstWebRTCICEGatheringState new_state;
1376 new_state = _collate_ice_gathering_states (webrtc);
1378 /* If the new state is complete, before we update the public state,
1379 * check if anyone published more ICE candidates while we were collating
1380 * and stop if so, because it means there's a new later
1381 * ice_gathering_state_task queued */
1382 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1384 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1385 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1386 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1388 ICE_UNLOCK (webrtc);
1391 if (new_state != webrtc->ice_gathering_state) {
1392 gchar *old_s, *new_s;
1394 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1396 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1398 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1399 old_s, old_state, new_s, new_state);
1403 webrtc->ice_gathering_state = new_state;
1405 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1413 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1415 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1419 static GstStructure *
1420 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1422 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1423 GstWebRTCICEConnectionState new_state;
1425 new_state = _collate_ice_connection_states (webrtc);
1427 if (new_state != old_state) {
1428 gchar *old_s, *new_s;
1430 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1432 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1434 GST_INFO_OBJECT (webrtc,
1435 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1440 webrtc->ice_connection_state = new_state;
1442 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1450 _update_ice_connection_state (GstWebRTCBin * webrtc)
1452 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1456 static GstStructure *
1457 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1459 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1460 GstWebRTCPeerConnectionState new_state;
1462 new_state = _collate_peer_connection_states (webrtc);
1464 if (new_state != old_state) {
1465 gchar *old_s, *new_s;
1467 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1469 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1471 GST_INFO_OBJECT (webrtc,
1472 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1477 webrtc->peer_connection_state = new_state;
1479 g_object_notify (G_OBJECT (webrtc), "connection-state");
1487 _update_peer_connection_state (GstWebRTCBin * webrtc)
1489 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1494 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1497 gboolean res = FALSE;
1499 GST_OBJECT_LOCK (webrtc);
1500 l = GST_ELEMENT (webrtc)->pads;
1501 for (; l; l = g_list_next (l)) {
1502 GstWebRTCBinPad *wpad;
1504 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1507 wpad = GST_WEBRTC_BIN_PAD (l->data);
1508 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1509 && (!wpad->trans || !wpad->trans->stopped)) {
1510 if (wpad->trans && wpad->trans->codec_preferences) {
1518 l = webrtc->priv->pending_pads;
1519 for (; l; l = g_list_next (l)) {
1520 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1528 GST_OBJECT_UNLOCK (webrtc);
1532 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1534 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1538 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1540 /* We can't negotiate until we have received caps on all our sink pads,
1541 * as we will need the ssrcs in our offer / answer */
1542 if (!_all_sinks_have_caps (webrtc)) {
1543 GST_LOG_OBJECT (webrtc,
1544 "no negotiation possible until caps have been received on all sink pads");
1548 /* If any implementation-specific negotiation is required, as described at
1549 * the start of this section, return "true".
1551 /* FIXME: emit when input caps/format changes? */
1553 if (!webrtc->current_local_description) {
1554 GST_LOG_OBJECT (webrtc, "no local description set");
1558 if (!webrtc->current_remote_description) {
1559 GST_LOG_OBJECT (webrtc, "no remote description set");
1563 /* If connection has created any RTCDataChannel's, and no m= section has
1564 * been negotiated yet for data, return "true". */
1565 if (webrtc->priv->data_channels->len > 0) {
1566 if (_message_get_datachannel_index (webrtc->current_local_description->
1567 sdp) >= G_MAXUINT) {
1568 GST_LOG_OBJECT (webrtc,
1569 "no data channel media section and have %u " "transports",
1570 webrtc->priv->data_channels->len);
1575 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1576 GstWebRTCRTPTransceiver *trans;
1578 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1580 if (trans->stopped) {
1581 /* FIXME: If t is stopped and is associated with an m= section according to
1582 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1583 * rejected in connection's currentLocalDescription or
1584 * currentRemoteDescription , return "true". */
1585 GST_FIXME_OBJECT (webrtc,
1586 "check if the transceiver is rejected in descriptions");
1588 const GstSDPMedia *media;
1589 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1591 if (trans->mline == -1 || trans->mid == NULL) {
1592 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1593 " mid %s", i, trans, trans->mid);
1596 /* internal inconsistency */
1597 g_assert (trans->mline <
1598 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1599 g_assert (trans->mline <
1600 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1602 /* FIXME: msid handling
1603 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1604 * section in connection's currentLocalDescription doesn't contain an
1605 * "a=msid" line, return "true". */
1608 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1610 local_dir = _get_direction_from_media (media);
1613 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1615 remote_dir = _get_direction_from_media (media);
1617 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1618 /* If connection's currentLocalDescription if of type "offer", and
1619 * the direction of the associated m= section in neither the offer
1620 * nor answer matches t's direction, return "true". */
1622 if (local_dir != trans->direction && remote_dir != trans->direction) {
1623 gchar *local_str, *remote_str, *dir_str;
1626 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1629 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1632 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1635 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1636 "description (local %s remote %s)", dir_str, local_str,
1641 g_free (remote_str);
1645 } else if (webrtc->current_local_description->type ==
1646 GST_WEBRTC_SDP_TYPE_ANSWER) {
1647 GstWebRTCRTPTransceiverDirection intersect_dir;
1649 /* If connection's currentLocalDescription if of type "answer", and
1650 * the direction of the associated m= section in the answer does not
1651 * match t's direction intersected with the offered direction (as
1652 * described in [JSEP] (section 5.3.1.)), return "true". */
1654 /* remote is the offer, local is the answer */
1655 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1657 if (intersect_dir != trans->direction) {
1658 gchar *local_str, *remote_str, *inter_str, *dir_str;
1661 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1664 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1667 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1670 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1673 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1674 "description intersected direction %s (local %s remote %s)",
1675 dir_str, local_str, inter_str, remote_str);
1679 g_free (remote_str);
1688 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1692 static GstStructure *
1693 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1695 if (webrtc->priv->need_negotiation) {
1696 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1698 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1706 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1708 _update_need_negotiation (GstWebRTCBin * webrtc)
1710 /* If connection's [[isClosed]] slot is true, abort these steps. */
1711 if (webrtc->priv->is_closed)
1713 /* If connection's signaling state is not "stable", abort these steps. */
1714 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1717 /* If the result of checking if negotiation is needed is "false", clear the
1718 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1719 * to false, and abort these steps. */
1720 if (!_check_if_negotiation_is_needed (webrtc)) {
1721 webrtc->priv->need_negotiation = FALSE;
1724 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1725 if (webrtc->priv->need_negotiation)
1727 /* Set connection's [[needNegotiation]] slot to true. */
1728 webrtc->priv->need_negotiation = TRUE;
1729 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1730 * true, fire a simple event named negotiationneeded at connection. */
1731 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1736 _query_pad_caps (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * rtp_trans,
1737 GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
1742 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1743 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1745 /* Only return an error if actual empty caps were returned from the query. */
1746 if (gst_caps_is_empty (caps)) {
1747 g_set_error (error, GST_WEBRTC_ERROR,
1748 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
1749 "Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
1750 gst_clear_caps (&caps);
1751 gst_caps_unref (filter);
1755 n = gst_caps_get_size (caps);
1757 /* Make sure the caps are complete enough to figure out the media type and
1758 * encoding-name, otherwise they would match with basically any media. */
1759 caps = gst_caps_make_writable (caps);
1760 for (i = n; i > 0; i--) {
1761 const GstStructure *s = gst_caps_get_structure (caps, i - 1);
1763 if (!gst_structure_has_name (s, "application/x-rtp") ||
1764 !gst_structure_has_field (s, "media") ||
1765 !gst_structure_has_field (s, "encoding-name")) {
1766 gst_caps_remove_structure (caps, i - 1);
1771 /* If the filtering above resulted in empty caps, or the caps were ANY to
1772 * begin with, then don't report and error but just NULL.
1774 * This would be the case if negotiation would not fail but the peer does
1775 * not have any specific enough preferred caps that would allow us to
1778 if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
1779 GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
1780 gst_clear_caps (&caps);
1783 gst_caps_unref (filter);
1789 _find_codec_preferences (GstWebRTCBin * webrtc,
1790 GstWebRTCRTPTransceiver * rtp_trans, guint media_idx, GError ** error)
1792 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1793 GstCaps *ret = NULL;
1794 GstCaps *codec_preferences = NULL;
1795 GstWebRTCBinPad *pad = NULL;
1796 GstPadDirection direction;
1798 g_assert (rtp_trans);
1799 g_assert (error && *error == NULL);
1801 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1804 GST_OBJECT_LOCK (rtp_trans);
1805 if (rtp_trans->codec_preferences) {
1806 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1807 rtp_trans->codec_preferences);
1808 codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
1810 GST_OBJECT_UNLOCK (rtp_trans);
1812 if (rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
1813 direction = GST_PAD_SRC;
1815 direction = GST_PAD_SINK;
1817 pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
1819 /* try to find a pad */
1821 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1823 /* For the case where we have set our transceiver to sendrecv, but the
1824 * sink pad has not been requested yet.
1827 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1829 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1831 /* try to find a pad */
1833 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
1837 GstCaps *caps = NULL;
1839 if (pad->received_caps) {
1840 caps = gst_caps_ref (pad->received_caps);
1842 static GstStaticCaps static_filter =
1843 GST_STATIC_CAPS ("application/x-rtp, "
1844 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1845 GstCaps *filter = gst_static_caps_get (&static_filter);
1847 filter = gst_caps_make_writable (filter);
1849 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
1850 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
1851 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
1852 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
1854 caps = _query_pad_caps (webrtc, rtp_trans, pad, filter, error);
1856 gst_object_unref (pad);
1862 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1863 GstWebRTCBinPad *srcpad =
1864 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1867 caps = _query_pad_caps (webrtc, rtp_trans, srcpad, caps, error);
1868 gst_object_unref (srcpad);
1875 if (caps && codec_preferences) {
1876 GstCaps *intersection;
1878 intersection = gst_caps_intersect_full (codec_preferences, caps,
1879 GST_CAPS_INTERSECT_FIRST);
1880 gst_clear_caps (&caps);
1882 if (gst_caps_is_empty (intersection)) {
1883 g_set_error (error, GST_WEBRTC_ERROR,
1884 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
1885 "Caps negotiation on pad %s failed against codec preferences",
1886 GST_PAD_NAME (pad));
1887 gst_clear_caps (&intersection);
1889 caps = intersection;
1895 gst_caps_replace (&trans->last_configured_caps, caps);
1902 if (codec_preferences)
1903 ret = gst_caps_ref (codec_preferences);
1904 else if (trans->last_configured_caps)
1905 ret = gst_caps_ref (trans->last_configured_caps);
1910 if (codec_preferences)
1911 gst_caps_unref (codec_preferences);
1914 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1920 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1921 WebRTCTransceiver * trans, const GstCaps * caps)
1930 ret = gst_caps_make_writable (caps);
1932 kind = webrtc_kind_from_caps (ret);
1933 for (i = 0; i < gst_caps_get_size (ret); i++) {
1934 GstStructure *s = gst_caps_get_structure (ret, i);
1937 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1938 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1940 if (kind == GST_WEBRTC_KIND_VIDEO
1941 && !gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1942 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1943 if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1944 gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
1946 /* FIXME: codec-specific parameters? */
1953 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1954 GParamSpec * pspec, GstWebRTCBin * webrtc)
1956 _update_ice_connection_state (webrtc);
1957 _update_peer_connection_state (webrtc);
1961 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1962 GParamSpec * pspec, GstWebRTCBin * webrtc)
1964 _update_ice_gathering_state (webrtc);
1968 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1969 GParamSpec * pspec, GstWebRTCBin * webrtc)
1971 _update_peer_connection_state (webrtc);
1975 match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
1977 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1979 return (trans->current_ssrc == GPOINTER_TO_UINT (data));
1983 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
1984 gboolean early, gpointer user_data)
1986 GstWebRTCBin *webrtc = user_data;
1987 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
1988 GstRTCPPacket packet;
1990 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
1993 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
1994 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
1996 GstWebRTCRTPTransceiver *rtp_trans;
1997 WebRTCTransceiver *trans;
1999 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
2002 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
2004 trans = (WebRTCTransceiver *) rtp_trans;
2006 if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
2008 gchar *pad_name = NULL;
2011 g_strdup_printf ("send_rtcp_src_%u",
2012 rtp_trans->sender->transport->session_id);
2013 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
2016 gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
2017 gst_object_unref (pad);
2023 gst_rtcp_buffer_unmap (&rtcp);
2026 /* False means we don't care about suppression */
2031 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
2033 GObject *internal_session = NULL;
2035 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
2036 session_id, &internal_session);
2038 if (internal_session) {
2039 g_signal_connect (internal_session, "on-sending-rtcp",
2040 G_CALLBACK (_on_sending_rtcp), webrtc);
2041 g_object_unref (internal_session);
2046 weak_free (GWeakRef * weak)
2048 g_weak_ref_clear (weak);
2052 static GstPadProbeReturn
2053 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
2055 GstWebRTCBin *webrtc = g_weak_ref_get ((GWeakRef *) user_data);
2058 return GST_PAD_PROBE_REMOVE;
2060 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
2061 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
2062 const GstStructure *s =
2063 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
2065 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
2069 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2070 GstWebRTCRTPTransceiver *rtp_trans;
2072 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
2075 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
2076 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
2077 trans->stream->session_id);
2080 /* Set DSCP field based on
2081 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2083 switch (rtp_trans->sender->priority) {
2084 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2087 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2090 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2091 switch (rtp_trans->kind) {
2092 case GST_WEBRTC_KIND_AUDIO:
2095 case GST_WEBRTC_KIND_VIDEO:
2096 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
2098 case GST_WEBRTC_KIND_UNKNOWN:
2103 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2104 switch (rtp_trans->kind) {
2105 case GST_WEBRTC_KIND_AUDIO:
2108 case GST_WEBRTC_KIND_VIDEO:
2109 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
2111 case GST_WEBRTC_KIND_UNKNOWN:
2118 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
2120 } else if (gst_structure_get_enum (s, "sctp-priority",
2121 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
2124 /* Set DSCP field based on
2125 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2128 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2131 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2134 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2135 dscp = 10; /* AF11 */
2137 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2138 dscp = 18; /* AF21 */
2141 if (webrtc->priv->data_channel_transport)
2142 gst_webrtc_ice_set_tos (webrtc->priv->ice,
2143 webrtc->priv->data_channel_transport->stream, dscp << 2);
2148 gst_object_unref (webrtc);
2150 return GST_PAD_PROBE_OK;
2153 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
2156 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
2158 GstWebRTCPriorityType sctp_priority = 0;
2161 if (!webrtc->priv->sctp_transport)
2165 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2166 GstWebRTCDataChannel *channel
2167 = g_ptr_array_index (webrtc->priv->data_channels, i);
2169 sctp_priority = MAX (sctp_priority, channel->priority);
2173 /* Default priority is low means DSCP field is left as 0 */
2174 if (sctp_priority == 0)
2175 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
2177 /* Nobody asks for DSCP, leave it as-is */
2178 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
2179 !webrtc->priv->tos_attached)
2182 /* If one stream has a non-default priority, then everyone else does too */
2183 gst_webrtc_bin_attach_tos (webrtc);
2185 webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
2190 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
2191 GstWebRTCICETransport * transport)
2196 pad = gst_element_get_static_pad (transport->sink, "sink");
2198 weak = g_new0 (GWeakRef, 1);
2199 g_weak_ref_init (weak, webrtc);
2201 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2202 _nicesink_pad_probe, weak, (GDestroyNotify) weak_free);
2203 gst_object_unref (pad);
2207 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
2211 if (webrtc->priv->tos_attached)
2213 webrtc->priv->tos_attached = TRUE;
2215 for (i = 0; i < webrtc->priv->transports->len; i++) {
2216 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
2218 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
2220 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
2221 stream->transport->transport);
2224 gst_webrtc_bin_update_sctp_priority (webrtc);
2227 static WebRTCTransceiver *
2228 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
2229 GstWebRTCRTPTransceiverDirection direction, guint mline, GstWebRTCKind kind,
2230 GstCaps * codec_preferences)
2232 char *dir_str = gst_webrtc_rtp_transceiver_direction_to_string (direction);
2233 WebRTCTransceiver *trans;
2234 GstWebRTCRTPTransceiver *rtp_trans;
2235 GstWebRTCRTPSender *sender;
2236 GstWebRTCRTPReceiver *receiver;
2238 sender = gst_webrtc_rtp_sender_new ();
2239 receiver = gst_webrtc_rtp_receiver_new ();
2240 trans = webrtc_transceiver_new (webrtc, sender, receiver);
2241 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2242 rtp_trans->direction = direction;
2243 rtp_trans->mline = mline;
2244 rtp_trans->kind = kind;
2245 rtp_trans->codec_preferences =
2246 codec_preferences ? gst_caps_ref (codec_preferences) : NULL;
2247 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
2248 rtp_trans->stopped = FALSE;
2250 GST_LOG_OBJECT (webrtc, "created new transceiver %" GST_PTR_FORMAT " with "
2251 "direction %s (%d), mline %u, kind %s (%d)", rtp_trans, dir_str,
2252 direction, mline, gst_webrtc_kind_to_string (kind), kind);
2254 g_signal_connect_object (sender, "notify::priority",
2255 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
2257 g_ptr_array_add (webrtc->priv->transceivers, trans);
2259 gst_object_unref (sender);
2260 gst_object_unref (receiver);
2262 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
2270 static TransportStream *
2271 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2273 GstWebRTCDTLSTransport *transport;
2274 TransportStream *ret;
2277 /* FIXME: how to parametrize the sender and the receiver */
2278 ret = transport_stream_new (webrtc, session_id);
2279 transport = ret->transport;
2281 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2282 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2283 g_signal_connect (G_OBJECT (transport->transport),
2284 "notify::gathering-state",
2285 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2286 g_signal_connect (G_OBJECT (transport), "notify::state",
2287 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2288 if (webrtc->priv->tos_attached)
2289 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2291 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2292 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2293 g_ptr_array_add (webrtc->priv->transports, ret);
2295 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2296 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2297 GST_ELEMENT (webrtc->rtpbin), pad_name))
2298 g_warn_if_reached ();
2301 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2302 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2303 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2304 g_warn_if_reached ();
2307 GST_TRACE_OBJECT (webrtc,
2308 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2313 static TransportStream *
2314 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2316 TransportStream *ret;
2318 ret = _find_transport_for_session (webrtc, session_id);
2321 ret = _create_transport_channel (webrtc, session_id);
2323 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2324 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2329 /* this is called from the webrtc thread with the pc lock held */
2331 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2332 GParamSpec * pspec, GstWebRTCBin * webrtc)
2334 GstWebRTCDataChannelState ready_state;
2336 g_object_get (channel, "ready-state", &ready_state, NULL);
2338 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2342 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2343 if (found == FALSE) {
2344 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2349 g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2352 gst_webrtc_bin_update_sctp_priority (webrtc);
2354 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2356 } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2360 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2361 || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2363 if (found == FALSE) {
2364 GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2371 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2372 GstWebRTCBin * webrtc)
2374 WebRTCDataChannel *channel;
2378 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2382 channel = _find_data_channel_for_id (webrtc, stream_id);
2384 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2385 channel->parent.id = stream_id;
2386 channel->webrtcbin = webrtc;
2388 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
2389 gst_bin_add (GST_BIN (webrtc), channel->appsink);
2391 gst_element_sync_state_with_parent (channel->appsrc);
2392 gst_element_sync_state_with_parent (channel->appsink);
2394 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2396 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2400 g_signal_connect (channel, "notify::ready-state",
2401 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2403 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2404 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2405 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2406 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2407 gst_object_unref (sink_pad);
2411 _on_sctp_state_notify (WebRTCSCTPTransport * sctp, GParamSpec * pspec,
2412 GstWebRTCBin * webrtc)
2414 GstWebRTCSCTPTransportState state;
2416 g_object_get (sctp, "state", &state, NULL);
2418 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2421 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2424 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2425 WebRTCDataChannel *channel;
2427 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2429 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2431 if (!channel->parent.negotiated && !channel->opened)
2432 webrtc_data_channel_start_negotiation (channel);
2438 /* Forward declaration so we can easily disconnect the signal handler */
2439 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2440 GParamSpec * pspec, GstWebRTCBin * webrtc);
2442 static GstStructure *
2443 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2445 TransportStream *stream;
2446 GstWebRTCDTLSTransport *transport;
2447 GstWebRTCDTLSTransportState dtls_state;
2448 WebRTCSCTPTransport *sctp_transport;
2450 stream = webrtc->priv->data_channel_transport;
2451 transport = stream->transport;
2453 g_object_get (transport, "state", &dtls_state, NULL);
2454 /* Not connected yet so just return */
2455 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2456 GST_DEBUG_OBJECT (webrtc,
2457 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2461 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2462 sctp_transport = webrtc->priv->sctp_transport;
2464 /* Not locked state anymore so this was already taken care of before */
2465 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2468 /* Start up the SCTP elements now that the DTLS connection is established */
2469 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2470 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2472 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2473 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2475 if (sctp_transport->sctpdec_block_id) {
2476 GstPad *receive_srcpad;
2479 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2481 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2483 sctp_transport->sctpdec_block_id = 0;
2484 gst_object_unref (receive_srcpad);
2487 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2494 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2495 GParamSpec * pspec, GstWebRTCBin * webrtc)
2497 GstWebRTCDTLSTransportState dtls_state;
2499 g_object_get (transport, "state", &dtls_state, NULL);
2501 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2504 /* Connected now, so schedule a task to update the state of the SCTP
2506 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2507 gst_webrtc_bin_enqueue_task (webrtc,
2508 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2512 static GstPadProbeReturn
2513 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2515 /* Drop all events: we don't care about them and don't want to block on
2516 * them. Sticky events would be forwarded again later once we unblock
2517 * and we don't want to forward them here already because that might
2518 * cause a spurious GST_FLOW_FLUSHING */
2519 if (GST_IS_EVENT (info->data))
2520 return GST_PAD_PROBE_DROP;
2522 /* But block on any actual data-flow so we don't accidentally send that
2523 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2526 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2528 return GST_PAD_PROBE_OK;
2531 static TransportStream *
2532 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2534 if (!webrtc->priv->data_channel_transport) {
2535 TransportStream *stream;
2536 WebRTCSCTPTransport *sctp_transport;
2538 stream = _find_transport_for_session (webrtc, session_id);
2541 stream = _create_transport_channel (webrtc, session_id);
2543 webrtc->priv->data_channel_transport = stream;
2545 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2546 sctp_transport = webrtc_sctp_transport_new ();
2547 sctp_transport->transport =
2548 g_object_ref (webrtc->priv->data_channel_transport->transport);
2549 sctp_transport->webrtcbin = webrtc;
2551 /* Don't automatically start SCTP elements as part of webrtcbin. We
2552 * need to delay this until the DTLS transport is fully connected! */
2553 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2554 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2556 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2557 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2560 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2561 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2562 g_signal_connect (sctp_transport, "notify::state",
2563 G_CALLBACK (_on_sctp_state_notify), webrtc);
2565 if (sctp_transport->sctpdec_block_id == 0) {
2566 GstPad *receive_srcpad;
2568 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2570 sctp_transport->sctpdec_block_id =
2571 gst_pad_add_probe (receive_srcpad,
2572 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2573 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2574 gst_object_unref (receive_srcpad);
2577 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2578 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2579 g_warn_if_reached ();
2581 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2582 GST_ELEMENT (stream->send_bin), "data_sink"))
2583 g_warn_if_reached ();
2585 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2586 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2588 if (!webrtc->priv->sctp_transport) {
2589 /* Connect to the notify::state signal to get notified when the DTLS
2590 * connection is established. Only then can we start the SCTP elements */
2591 g_signal_connect (stream->transport, "notify::state",
2592 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2594 /* As this would be racy otherwise, also schedule a task that checks the
2595 * current state of the connection already without getting the signal
2597 gst_webrtc_bin_enqueue_task (webrtc,
2598 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2601 webrtc->priv->sctp_transport = sctp_transport;
2603 gst_webrtc_bin_update_sctp_priority (webrtc);
2606 return webrtc->priv->data_channel_transport;
2609 static TransportStream *
2610 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2611 gboolean is_datachannel)
2614 return _get_or_create_data_channel_transports (webrtc, session_id);
2616 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2620 g_array_find_uint (GArray * array, guint val)
2624 for (i = 0; i < array->len; i++) {
2625 if (g_array_index (array, guint, i) == val)
2633 _pick_available_pt (GArray * reserved_pts, guint * i)
2635 gboolean ret = FALSE;
2637 for (*i = 96; *i <= 127; (*i)++) {
2638 if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
2639 g_array_append_val (reserved_pts, *i);
2649 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2650 GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
2651 GstSDPMedia * media)
2653 gboolean ret = TRUE;
2655 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2658 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2662 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2665 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2667 str = g_strdup_printf ("%u", pt);
2668 gst_sdp_media_add_format (media, str);
2670 str = g_strdup_printf ("%u red/%d", pt, clockrate);
2671 gst_sdp_media_add_attribute (media, "rtpmap", str);
2674 *rtx_target_pt = pt;
2676 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2679 str = g_strdup_printf ("%u", pt);
2680 gst_sdp_media_add_format (media, str);
2682 str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
2683 gst_sdp_media_add_attribute (media, "rtpmap", str);
2692 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2693 GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
2694 GstSDPMedia * media)
2696 gboolean ret = TRUE;
2698 if (trans->local_rtx_ssrc_map)
2699 gst_structure_free (trans->local_rtx_ssrc_map);
2701 trans->local_rtx_ssrc_map =
2702 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2704 if (trans->do_nack) {
2708 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2711 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2713 str = g_strdup_printf ("%u", target_ssrc);
2714 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2715 g_random_int (), NULL);
2718 str = g_strdup_printf ("%u", pt);
2719 gst_sdp_media_add_format (media, str);
2722 str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
2723 gst_sdp_media_add_attribute (media, "rtpmap", str);
2726 str = g_strdup_printf ("%u apt=%d", pt, target_pt);
2727 gst_sdp_media_add_attribute (media, "fmtp", str);
2735 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2737 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2738 GstSDPMedia * media)
2743 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2744 g_value_get_uint (value));
2745 gst_sdp_media_add_attribute (media, "ssrc-group", str);
2755 GstWebRTCBin *webrtc;
2756 WebRTCTransceiver *trans;
2760 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2766 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2767 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2768 cname = gst_structure_get_string (sdes, "cname");
2770 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2772 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2773 cname, GST_OBJECT_NAME (data->trans));
2774 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2777 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2778 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2781 gst_structure_free (sdes);
2787 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2788 WebRTCTransceiver * trans)
2791 RtxSsrcData data = { media, webrtc, trans };
2795 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2796 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2797 cname = gst_structure_get_string (sdes, "cname");
2799 if (trans->local_rtx_ssrc_map)
2800 gst_structure_foreach (trans->local_rtx_ssrc_map,
2801 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2803 for (i = 0; i < gst_caps_get_size (caps); i++) {
2804 const GstStructure *s = gst_caps_get_structure (caps, i);
2807 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2810 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2812 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2813 GST_OBJECT_NAME (trans));
2814 gst_sdp_media_add_attribute (media, "ssrc", str);
2817 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2818 gst_sdp_media_add_attribute (media, "ssrc", str);
2823 gst_structure_free (sdes);
2825 if (trans->local_rtx_ssrc_map)
2826 gst_structure_foreach (trans->local_rtx_ssrc_map,
2827 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2831 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2832 GstSDPMedia * media)
2834 gchar *cert, *fingerprint, *val;
2836 g_object_get (transport, "certificate", &cert, NULL);
2839 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2842 g_strdup_printf ("%s %s",
2843 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2844 g_free (fingerprint);
2846 gst_sdp_media_add_attribute (media, "fingerprint", val);
2851 _parse_extmap (GQuark field_id, const GValue * value, GError ** error)
2855 if (G_VALUE_HOLDS_STRING (value)) {
2856 ret = g_value_dup_string (value);
2857 } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
2858 && gst_value_array_get_size (value) == 3) {
2860 const gchar *direction, *extensionname, *extensionattributes;
2862 val = gst_value_array_get_value (value, 0);
2863 direction = g_value_get_string (val);
2865 val = gst_value_array_get_value (value, 1);
2866 extensionname = g_value_get_string (val);
2868 val = gst_value_array_get_value (value, 2);
2869 extensionattributes = g_value_get_string (val);
2871 if (!extensionname || *extensionname == '\0')
2874 if (direction && *direction != '\0' && extensionattributes
2875 && *extensionattributes != '\0') {
2877 g_strdup_printf ("/%s %s %s", direction, extensionname,
2878 extensionattributes);
2879 } else if (direction && *direction != '\0') {
2880 ret = g_strdup_printf ("/%s %s", direction, extensionname);
2881 } else if (extensionattributes && *extensionattributes != '\0') {
2882 ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
2884 ret = g_strdup (extensionname);
2888 if (!ret && error) {
2889 gchar *val_str = gst_value_serialize (value);
2891 g_set_error (error, GST_WEBRTC_ERROR,
2892 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
2893 "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
2904 GstStructure *extmap;
2909 _dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
2911 gboolean is_extmap =
2912 g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
2918 gchar *new_value = _parse_extmap (field_id, value, data->error);
2925 if (gst_structure_id_has_field (data->extmap, field_id)) {
2927 _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
2930 g_assert (old_value);
2932 if (g_strcmp0 (new_value, old_value)) {
2934 ("extmap contains different values for id %s (%s != %s)",
2935 g_quark_to_string (field_id), old_value, new_value);
2936 g_set_error (data->error, GST_WEBRTC_ERROR,
2937 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
2938 "extmap contains different values for id %s (%s != %s)",
2939 g_quark_to_string (field_id), old_value, new_value);
2948 gst_structure_id_set_value (data->extmap, field_id, value);
2958 static GstStructure *
2959 _gather_extmap (GstCaps * caps, GError ** error)
2962 { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
2965 n = gst_caps_get_size (caps);
2967 for (i = 0; i < n; i++) {
2968 GstStructure *s = gst_caps_get_structure (caps, i);
2970 gst_structure_filter_and_map_in_place (s,
2971 (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
2974 gst_clear_structure (&edata.extmap);
2979 return edata.extmap;
2983 _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
2985 gst_structure_id_set_value (s, field_id, value);
2990 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2992 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2993 const GstSDPMedia * last_media, GstWebRTCRTPTransceiver * trans,
2994 guint media_idx, GString * bundled_mids, guint bundle_idx,
2995 gchar * bundle_ufrag, gchar * bundle_pwd, GArray * reserved_pts,
2996 GHashTable * all_mids, GError ** error)
2999 * rtp header extensions
3006 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
3008 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3009 gchar *direction, *ufrag, *pwd, *mid;
3010 gboolean bundle_only;
3011 guint rtp_session_idx;
3013 GstStructure *extmap;
3016 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3019 g_assert (trans->mline == -1 || trans->mline == media_idx);
3021 rtp_session_idx = bundled_mids ? bundle_idx : media_idx;
3023 bundle_only = bundled_mids && bundle_idx != media_idx
3024 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
3026 caps = _find_codec_preferences (webrtc, trans, media_idx, error);
3027 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3030 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
3031 gst_clear_caps (&caps);
3036 n = gst_sdp_media_formats_len (last_media);
3038 caps = gst_caps_new_empty ();
3039 for (i = 0; i < n; i++) {
3040 guint fmt = atoi (gst_sdp_media_get_format (last_media, i));
3041 GstCaps *tmp = gst_sdp_media_get_caps_from_media (last_media, fmt);
3042 GstStructure *s = gst_caps_get_structure (tmp, 0);
3043 gst_structure_set_name (s, "application/x-rtp");
3044 gst_caps_append_structure (caps, gst_structure_copy (s));
3045 gst_clear_caps (&tmp);
3047 GST_DEBUG_OBJECT (webrtc, "using previously negotiated caps for "
3048 "transceiver %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, trans, caps);
3053 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver %"
3054 GST_PTR_FORMAT ", skipping", trans);
3060 const char *setup = gst_sdp_media_get_attribute_val (last_media, "setup");
3062 gst_sdp_media_add_attribute (media, "setup", setup);
3064 g_set_error (error, GST_WEBRTC_ERROR,
3065 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3066 "media %u cannot renegotiate without an existing a=setup line",
3071 /* mandated by JSEP */
3072 gst_sdp_media_add_attribute (media, "setup", "actpass");
3075 /* FIXME: deal with ICE restarts */
3076 if (last_offer && trans->mline != -1 && trans->mid) {
3077 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
3078 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
3079 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
3081 GST_DEBUG_OBJECT (trans,
3082 "%u Generating new ice parameters mline %i, mid %s", media_idx,
3083 trans->mline, trans->mid);
3084 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3085 _generate_ice_credentials (&ufrag, &pwd);
3087 g_assert (bundle_ufrag && bundle_pwd);
3088 ufrag = g_strdup (bundle_ufrag);
3089 pwd = g_strdup (bundle_pwd);
3093 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3094 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3098 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
3099 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3100 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3103 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3106 /* FIXME: negotiate this */
3107 /* FIXME: when bundle_only, these should not be added:
3108 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
3109 * However, this causes incompatibilities with current versions
3110 * of the major browsers */
3111 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
3112 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
3115 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
3117 gst_sdp_media_add_attribute (media, direction, "");
3120 caps = gst_caps_make_writable (caps);
3122 /* When an extmap is defined twice for the same ID, firefox complains and
3123 * errors out (chrome is smart enough to accept strict duplicates).
3125 * To work around this, we deduplicate extmap attributes, and also error
3126 * out when a different extmap is defined for the same ID.
3128 * _gather_extmap will strip out all extmap- fields, which will then be
3129 * added upon adding the first format for the media.
3131 extmap = _gather_extmap (caps, error);
3134 GST_ERROR_OBJECT (webrtc,
3135 "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
3136 gst_caps_unref (caps);
3140 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3143 for (i = 0; i < gst_caps_get_size (caps); i++) {
3144 GstCaps *format = gst_caps_new_empty ();
3145 GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
3148 gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
3151 gst_caps_append_structure (format, s);
3153 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
3154 " to %u-th media", i, format, media_idx);
3156 /* this only looks at the first structure so we loop over the given caps
3157 * and add each structure inside it piecemeal */
3158 gst_sdp_media_set_media_from_caps (format, media);
3160 gst_caps_unref (format);
3163 gst_clear_structure (&extmap);
3166 const GstStructure *s = gst_caps_get_structure (caps, 0);
3167 gint clockrate = -1;
3169 gint original_rtx_target_pt; /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
3170 guint rtx_target_ssrc = -1;
3172 if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
3173 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3174 g_array_append_val (reserved_pts, rtx_target_pt);
3176 original_rtx_target_pt = rtx_target_pt;
3178 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
3179 GST_WARNING_OBJECT (webrtc,
3180 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
3181 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
3182 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
3185 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3186 clockrate, &rtx_target_pt, media);
3187 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3188 clockrate, rtx_target_pt, rtx_target_ssrc, media);
3189 if (original_rtx_target_pt != rtx_target_pt)
3190 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3191 clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
3194 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
3196 /* Some identifier; we also add the media name to it so it's identifiable */
3198 const char *media_mid = gst_sdp_media_get_attribute_val (media, "mid");
3200 gst_sdp_media_add_attribute (media, "mid", trans->mid);
3201 } else if (g_strcmp0 (media_mid, trans->mid) != 0) {
3202 g_set_error (error, GST_WEBRTC_ERROR,
3203 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3204 "Cannot change media %u mid value from \'%s\' to \'%s\'",
3205 media_idx, media_mid, trans->mid);
3208 mid = g_strdup (trans->mid);
3210 const GstStructure *s = gst_caps_get_structure (caps, 0);
3212 mid = g_strdup (gst_structure_get_string (s, "a-mid"));
3215 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3216 g_set_error (error, GST_WEBRTC_ERROR,
3217 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3218 "Cannot re-use mid \'%s\' from the caps in m= line %u that has "
3219 "already been used for a previous m= line in the SDP", mid,
3223 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3225 /* Make sure to avoid mid collisions */
3227 mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3228 webrtc->priv->media_counter++);
3229 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3232 gst_sdp_media_add_attribute (media, "mid", mid);
3233 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3241 * - add a=candidate lines for gathered candidates
3244 if (trans->sender) {
3245 if (!trans->sender->transport) {
3246 TransportStream *item;
3248 item = _get_or_create_transport_stream (webrtc, rtp_session_idx, FALSE);
3250 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
3253 _add_fingerprint_to_media (trans->sender->transport, media);
3258 g_string_append_printf (bundled_mids, " %s", mid);
3261 g_clear_pointer (&mid, g_free);
3263 gst_caps_unref (caps);
3269 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
3271 if (pad->received_caps) {
3272 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
3275 if (gst_structure_get_int (s, "payload", &pt)) {
3276 GST_TRACE_OBJECT (pad, "have reserved pt %u from received caps", pt);
3277 g_array_append_val (reserved_pts, pt);
3283 gather_reserved_pts (GstWebRTCBin * webrtc)
3285 GstElement *element = GST_ELEMENT (webrtc);
3286 GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3289 GST_OBJECT_LOCK (webrtc);
3290 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
3291 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
3294 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3295 GstWebRTCRTPTransceiver *trans;
3297 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3298 GST_OBJECT_LOCK (trans);
3299 if (trans->codec_preferences) {
3303 n = gst_caps_get_size (trans->codec_preferences);
3304 for (j = 0; j < n; j++) {
3305 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
3306 if (gst_structure_get_int (s, "payload", &pt)) {
3307 GST_TRACE_OBJECT (trans, "have reserved pt %u from codec preferences",
3309 g_array_append_val (reserved_pts, pt);
3313 GST_OBJECT_UNLOCK (trans);
3315 GST_OBJECT_UNLOCK (webrtc);
3317 return reserved_pts;
3321 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
3322 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
3323 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
3325 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3326 gchar *ufrag, *pwd, *sdp_mid;
3327 gboolean bundle_only = bundled_mids
3328 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
3329 && gst_sdp_message_medias_len (msg) != bundle_idx;
3330 guint last_data_index = G_MAXUINT;
3332 /* add data channel support */
3333 if (webrtc->priv->data_channels->len == 0)
3337 last_data_index = _message_get_datachannel_index (last_offer);
3338 if (last_data_index < G_MAXUINT) {
3339 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
3340 /* XXX: is this always true when recycling transceivers?
3341 * i.e. do we always put the data channel in the same mline */
3342 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
3346 /* mandated by JSEP */
3347 gst_sdp_media_add_attribute (media, "setup", "actpass");
3349 /* FIXME: only needed when restarting ICE */
3350 if (last_offer && last_data_index < G_MAXUINT) {
3351 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
3352 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
3354 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3355 _generate_ice_credentials (&ufrag, &pwd);
3357 ufrag = g_strdup (bundle_ufrag);
3358 pwd = g_strdup (bundle_pwd);
3361 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3362 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3366 gst_sdp_media_set_media (media, "application");
3367 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
3368 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3369 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3370 gst_sdp_media_add_format (media, "webrtc-datachannel");
3372 if (bundle_idx != gst_sdp_message_medias_len (msg))
3373 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3375 if (last_offer && last_data_index < G_MAXUINT) {
3376 const GstSDPMedia *last_data_media;
3379 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
3380 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
3382 gst_sdp_media_add_attribute (media, "mid", mid);
3384 /* Make sure to avoid mid collisions */
3386 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3387 webrtc->priv->media_counter++);
3388 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3391 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3392 g_hash_table_insert (all_mids, sdp_mid, NULL);
3399 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3402 g_string_append_printf (bundled_mids, " %s", mid);
3405 /* FIXME: negotiate this properly */
3406 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3408 _get_or_create_data_channel_transports (webrtc,
3409 bundled_mids ? 0 : webrtc->priv->transceivers->len);
3410 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
3415 /* TODO: use the options argument */
3416 static GstSDPMessage *
3417 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3420 GstSDPMessage *ret = NULL;
3421 GString *bundled_mids = NULL;
3422 gchar *bundle_ufrag = NULL;
3423 gchar *bundle_pwd = NULL;
3424 GArray *reserved_pts = NULL;
3425 GHashTable *all_mids =
3426 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3428 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3429 GList *seen_transceivers = NULL;
3430 guint media_idx = 0;
3433 gst_sdp_message_new (&ret);
3435 gst_sdp_message_set_version (ret, "0");
3438 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
3440 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
3441 sess_id = g_strdup (origin->sess_id);
3443 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
3445 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
3449 gst_sdp_message_set_session_name (ret, "-");
3450 gst_sdp_message_add_time (ret, "0", "0", NULL);
3451 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
3453 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
3454 bundled_mids = g_string_new ("BUNDLE");
3455 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
3456 bundled_mids = g_string_new ("BUNDLE");
3459 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3460 GStrv last_bundle = NULL;
3461 guint bundle_media_index;
3463 reserved_pts = gather_reserved_pts (webrtc);
3464 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
3465 && last_bundle && last_bundle[0]
3466 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
3468 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
3470 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
3472 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3475 g_strfreev (last_bundle);
3478 /* FIXME: recycle transceivers */
3480 /* Fill up the renegotiated streams first */
3482 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
3483 GstWebRTCRTPTransceiver *trans = NULL;
3484 const GstSDPMedia *last_media;
3486 last_media = gst_sdp_message_get_media (last_offer, i);
3488 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
3489 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
3490 const gchar *last_mid;
3492 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
3494 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3495 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3497 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
3498 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
3502 memset (&media, 0, sizeof (media));
3504 g_assert (!g_list_find (seen_transceivers, trans));
3506 if (wtrans->mline_locked && trans->mline != media_idx) {
3507 g_set_error (error, GST_WEBRTC_ERROR,
3508 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3509 "Previous negotiatied transceiver <%s> with mid %s was in "
3510 "mline %d but transceiver has locked mline %u",
3511 GST_OBJECT_NAME (trans), trans->mid, media_idx, trans->mline);
3515 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3516 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3517 trans->mid, media_idx);
3519 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3520 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3523 gst_sdp_media_init (&media);
3524 if (!sdp_media_from_transceiver (webrtc, &media, last_media, trans,
3525 media_idx, bundled_mids, 0, bundle_ufrag, bundle_pwd,
3526 reserved_pts, all_mids, error)) {
3527 gst_sdp_media_uninit (&media);
3529 g_set_error_literal (error, GST_WEBRTC_ERROR,
3530 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3531 "Could not reuse transceiver");
3534 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3535 g_array_free (reserved_pts, TRUE);
3536 reserved_pts = NULL;
3541 mid = gst_sdp_media_get_attribute_val (&media, "mid");
3542 g_assert (mid && g_strcmp0 (last_mid, mid) == 0);
3544 gst_sdp_message_add_media (ret, &media);
3547 gst_sdp_media_uninit (&media);
3548 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3552 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3553 "application") == 0) {
3554 GstSDPMedia media = { 0, };
3555 gst_sdp_media_init (&media);
3556 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3557 bundle_ufrag, bundle_pwd, all_mids)) {
3558 gst_sdp_message_add_media (ret, &media);
3561 gst_sdp_media_uninit (&media);
3567 /* First, go over all transceivers and gather existing mids */
3568 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3569 GstWebRTCRTPTransceiver *trans;
3571 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3573 if (g_list_find (seen_transceivers, trans))
3577 if (g_hash_table_contains (all_mids, trans->mid)) {
3578 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3579 "Duplicate mid %s when creating offer", trans->mid);
3583 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3588 /* add any extra streams */
3590 GstWebRTCRTPTransceiver *trans = NULL;
3591 GstSDPMedia media = { 0, };
3593 /* First find a transceiver requesting this m-line */
3594 trans = _find_transceiver_for_mline (webrtc, media_idx);
3597 /* We can't have seen it already, because it is locked to this line */
3598 g_assert (!g_list_find (seen_transceivers, trans));
3599 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3601 /* Otherwise find a free transceiver */
3602 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3603 WebRTCTransceiver *wtrans;
3605 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3606 wtrans = WEBRTC_TRANSCEIVER (trans);
3608 /* don't add transceivers twice */
3609 if (g_list_find (seen_transceivers, trans))
3612 /* Ignore transceivers with a locked mline, as they would have been
3613 * found above or will be used later */
3614 if (wtrans->mline_locked)
3617 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3618 /* don't add stopped transceivers */
3619 if (trans->stopped) {
3623 /* Otherwise take it */
3627 /* Stop if we got all transceivers */
3628 if (i == webrtc->priv->transceivers->len) {
3630 /* But try to add a data channel first, we do it here, because
3631 * it can allow a locked m-line to be put after, so we need to
3632 * do another iteration after.
3634 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3635 GstSDPMedia media = { 0, };
3636 gst_sdp_media_init (&media);
3637 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3638 bundle_ufrag, bundle_pwd, all_mids)) {
3639 gst_sdp_message_add_media (ret, &media);
3643 gst_sdp_media_uninit (&media);
3647 /* Verify that we didn't ignore any locked m-line transceivers */
3648 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3649 WebRTCTransceiver *wtrans;
3651 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3652 wtrans = WEBRTC_TRANSCEIVER (trans);
3653 /* don't add transceivers twice */
3654 if (g_list_find (seen_transceivers, trans))
3656 g_assert (wtrans->mline_locked);
3658 g_set_error (error, GST_WEBRTC_ERROR,
3659 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3660 "Tranceiver <%s> with mid %s has locked mline %d but the offer "
3661 "only has %u sections", GST_OBJECT_NAME (trans), trans->mid,
3662 trans->mline, media_idx);
3669 gst_sdp_media_init (&media);
3671 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3672 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3675 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3676 "index %u", trans, media_idx);
3678 if (sdp_media_from_transceiver (webrtc, &media, NULL, trans, media_idx,
3679 bundled_mids, 0, bundle_ufrag, bundle_pwd, reserved_pts, all_mids,
3681 /* as per JSEP, a=rtcp-mux-only is only added for new streams */
3682 gst_sdp_media_add_attribute (&media, "rtcp-mux-only", "");
3683 gst_sdp_message_add_media (ret, &media);
3686 gst_sdp_media_uninit (&media);
3689 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3690 g_array_free (reserved_pts, TRUE);
3691 reserved_pts = NULL;
3697 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3698 g_array_free (reserved_pts, TRUE);
3699 reserved_pts = NULL;
3702 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
3705 g_assert (media_idx == gst_sdp_message_medias_len (ret));
3708 gchar *mids = g_string_free (bundled_mids, FALSE);
3710 gst_sdp_message_add_attribute (ret, "group", mids);
3712 bundled_mids = NULL;
3715 /* FIXME: pre-emptively setup receiving elements when needed */
3717 if (webrtc->priv->last_generated_answer)
3718 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3719 webrtc->priv->last_generated_answer = NULL;
3720 if (webrtc->priv->last_generated_offer)
3721 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3723 GstSDPMessage *copy;
3724 gst_sdp_message_copy (ret, ©);
3725 webrtc->priv->last_generated_offer =
3726 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3731 g_array_free (reserved_pts, TRUE);
3733 g_hash_table_unref (all_mids);
3735 g_list_free (seen_transceivers);
3738 g_free (bundle_ufrag);
3741 g_free (bundle_pwd);
3744 g_string_free (bundled_mids, TRUE);
3749 gst_sdp_message_free (ret);
3755 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3756 gint * rtx_target_pt)
3760 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3763 for (i = 0; i < gst_caps_get_size (caps); i++) {
3764 const GstStructure *s = gst_caps_get_structure (caps, i);
3766 if (gst_structure_has_name (s, "application/x-rtp")) {
3767 const gchar *encoding_name =
3768 gst_structure_get_string (s, "encoding-name");
3772 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3773 gst_structure_get_int (s, "payload", &pt)) {
3774 if (!g_strcmp0 (encoding_name, "RED")) {
3777 str = g_strdup_printf ("%u", pt);
3778 gst_sdp_media_add_format (media, str);
3780 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3781 *rtx_target_pt = pt;
3782 gst_sdp_media_add_attribute (media, "rtpmap", str);
3784 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3787 str = g_strdup_printf ("%u", pt);
3788 gst_sdp_media_add_format (media, str);
3790 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3791 gst_sdp_media_add_attribute (media, "rtpmap", str);
3800 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3801 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3804 const GstStructure *s;
3806 if (trans->local_rtx_ssrc_map)
3807 gst_structure_free (trans->local_rtx_ssrc_map);
3809 trans->local_rtx_ssrc_map =
3810 gst_structure_new_empty ("application/x-rtp-ssrc-map");
3812 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
3813 s = gst_caps_get_structure (offer_caps, i);
3815 if (gst_structure_has_name (s, "application/x-rtp")) {
3816 const gchar *encoding_name =
3817 gst_structure_get_string (s, "encoding-name");
3818 const gchar *apt_str = gst_structure_get_string (s, "apt");
3826 apt = atoi (apt_str);
3828 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3829 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
3830 if (!g_strcmp0 (encoding_name, "RTX")) {
3833 str = g_strdup_printf ("%u", pt);
3834 gst_sdp_media_add_format (media, str);
3836 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
3837 gst_sdp_media_add_attribute (media, "rtpmap", str);
3840 str = g_strdup_printf ("%d apt=%d", pt, apt);
3841 gst_sdp_media_add_attribute (media, "fmtp", str);
3844 str = g_strdup_printf ("%u", target_ssrc);
3845 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
3846 g_random_int (), NULL);
3855 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
3856 const GstCaps * caps)
3858 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
3860 if (trans->kind == kind)
3863 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
3872 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
3873 guint * target_ssrc)
3875 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
3877 gst_structure_get_int (s, "payload", target_pt);
3878 gst_structure_get_uint (s, "ssrc", target_ssrc);
3881 /* TODO: use the options argument */
3882 static GstSDPMessage *
3883 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3886 GstSDPMessage *ret = NULL;
3887 const GstWebRTCSessionDescription *pending_remote =
3888 webrtc->pending_remote_description;
3890 GStrv bundled = NULL;
3891 guint bundle_idx = 0;
3892 GString *bundled_mids = NULL;
3893 gchar *bundle_ufrag = NULL;
3894 gchar *bundle_pwd = NULL;
3895 GList *seen_transceivers = NULL;
3896 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
3898 if (!webrtc->pending_remote_description) {
3899 g_set_error_literal (error, GST_WEBRTC_ERROR,
3900 GST_WEBRTC_ERROR_INVALID_STATE,
3901 "Asked to create an answer without a remote description");
3905 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
3909 GStrv last_bundle = NULL;
3910 guint bundle_media_index;
3912 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
3913 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
3914 "Bundle tag is %s but no media found matching", bundled[0]);
3918 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3919 bundled_mids = g_string_new ("BUNDLE");
3922 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
3923 && last_bundle && last_bundle[0]
3924 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
3926 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
3928 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
3930 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3933 g_strfreev (last_bundle);
3936 gst_sdp_message_new (&ret);
3938 gst_sdp_message_set_version (ret, "0");
3940 const GstSDPOrigin *offer_origin =
3941 gst_sdp_message_get_origin (pending_remote->sdp);
3942 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
3943 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
3945 gst_sdp_message_set_session_name (ret, "-");
3947 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
3948 const GstSDPAttribute *attr =
3949 gst_sdp_message_get_attribute (pending_remote->sdp, i);
3951 if (g_strcmp0 (attr->key, "ice-options") == 0) {
3952 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
3956 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
3957 GstSDPMedia *media = NULL;
3958 GstSDPMedia *offer_media;
3959 GstWebRTCDTLSSetup offer_setup, answer_setup;
3961 gboolean bundle_only;
3965 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
3966 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
3968 gst_sdp_media_new (&media);
3969 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3970 gst_sdp_media_set_port_info (media, 0, 0);
3972 gst_sdp_media_set_port_info (media, 9, 0);
3973 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3978 /* FIXME: deal with ICE restarts */
3979 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
3980 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
3981 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
3984 _generate_ice_credentials (&ufrag, &pwd);
3986 ufrag = g_strdup (bundle_ufrag);
3987 pwd = g_strdup (bundle_pwd);
3990 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3991 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3996 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
3997 const GstSDPAttribute *attr =
3998 gst_sdp_media_get_attribute (offer_media, j);
4000 if (g_strcmp0 (attr->key, "mid") == 0
4001 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
4002 gst_sdp_media_add_attribute (media, attr->key, attr->value);
4003 /* FIXME: handle anything we want to keep */
4007 mid = gst_sdp_media_get_attribute_val (media, "mid");
4008 /* XXX: not strictly required but a lot of functionality requires a mid */
4011 /* set the a=setup: attribute */
4012 offer_setup = _get_dtls_setup_from_media (offer_media);
4013 answer_setup = _intersect_dtls_setup (offer_setup);
4014 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4015 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
4016 "transceiver direction");
4019 _media_replace_setup (media, answer_setup);
4021 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
4024 if (gst_sdp_media_formats_len (offer_media) != 1) {
4025 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
4026 "for webrtc-datachannel");
4029 sctp_port = _get_sctp_port_from_media (offer_media);
4030 if (sctp_port == -1) {
4031 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
4035 /* XXX: older browsers will produce a different SDP format for data
4036 * channel that is currently not parsed correctly */
4037 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
4039 gst_sdp_media_set_media (media, "application");
4040 gst_sdp_media_set_port_info (media, 9, 0);
4041 gst_sdp_media_add_format (media, "webrtc-datachannel");
4043 /* FIXME: negotiate this properly on renegotiation */
4044 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
4046 _get_or_create_data_channel_transports (webrtc,
4047 bundled_mids ? bundle_idx : i);
4051 g_string_append_printf (bundled_mids, " %s", mid);
4054 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
4056 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
4057 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
4058 GstCaps *offer_caps, *answer_caps = NULL;
4059 GstWebRTCRTPTransceiver *rtp_trans = NULL;
4060 WebRTCTransceiver *trans = NULL;
4061 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
4062 gint target_pt = -1;
4063 gint original_target_pt = -1;
4064 guint target_ssrc = 0;
4066 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
4067 offer_caps = _rtp_caps_from_media (offer_media);
4069 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
4070 && (rtp_trans = _find_transceiver_for_mid (webrtc, mid))) {
4071 const GstSDPMedia *last_media =
4072 gst_sdp_message_get_media (last_answer, i);
4073 const gchar *last_mid =
4074 gst_sdp_media_get_attribute_val (last_media, "mid");
4075 GstCaps *current_caps;
4077 /* FIXME: assumes no shenanigans with recycling transceivers */
4078 g_assert (g_strcmp0 (mid, last_mid) == 0);
4080 current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4082 gst_caps_unref (offer_caps);
4086 current_caps = _rtp_caps_from_media (last_media);
4089 answer_caps = gst_caps_intersect (offer_caps, current_caps);
4090 if (gst_caps_is_empty (answer_caps)) {
4091 GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
4092 GST_PTR_FORMAT ") don't intersect with caps from codec"
4093 " preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
4095 gst_caps_unref (current_caps);
4096 gst_caps_unref (answer_caps);
4097 gst_caps_unref (offer_caps);
4100 gst_caps_unref (current_caps);
4103 /* XXX: In theory we're meant to use the sendrecv formats for the
4104 * inactive direction however we don't know what that may be and would
4105 * require asking outside what it expects to possibly send later */
4107 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
4108 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
4109 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
4111 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
4112 GstCaps *trans_caps;
4114 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
4116 if (g_list_find (seen_transceivers, rtp_trans)) {
4117 /* Don't double allocate a transceiver to multiple mlines */
4122 trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
4124 gst_caps_unref (offer_caps);
4128 GST_LOG_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4129 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4131 /* FIXME: technically this is a little overreaching as some fields we
4132 * we can deal with not having and/or we may have unrecognized fields
4133 * that we cannot actually support */
4135 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4136 gst_caps_unref (trans_caps);
4138 if (!gst_caps_is_empty (answer_caps)) {
4139 GST_LOG_OBJECT (webrtc,
4140 "found compatible transceiver %" GST_PTR_FORMAT
4141 " for offer media %u", rtp_trans, i);
4144 gst_caps_unref (answer_caps);
4153 answer_dir = rtp_trans->direction;
4154 g_assert (answer_caps != NULL);
4156 /* if no transceiver, then we only receive that stream and respond with
4157 * the intersection with the transceivers codec preferences caps */
4158 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
4159 GST_WARNING_OBJECT (webrtc, "did not find compatible transceiver for "
4160 "offer caps %" GST_PTR_FORMAT ", will only receive", offer_caps);
4164 GstCaps *trans_caps;
4165 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
4167 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
4168 kind = GST_WEBRTC_KIND_AUDIO;
4169 else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
4171 kind = GST_WEBRTC_KIND_VIDEO;
4173 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
4174 GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
4176 trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
4177 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4179 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
4180 " for mline %u with media kind %d", trans, i, kind);
4182 trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4184 gst_caps_unref (offer_caps);
4188 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4189 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4191 /* FIXME: technically this is a little overreaching as some fields we
4192 * we can deal with not having and/or we may have unrecognized fields
4193 * that we cannot actually support */
4195 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4196 gst_clear_caps (&trans_caps);
4198 answer_caps = gst_caps_ref (offer_caps);
4201 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4204 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
4206 if (gst_caps_is_empty (answer_caps)) {
4207 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
4208 gst_clear_caps (&answer_caps);
4209 gst_clear_caps (&offer_caps);
4213 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps)) {
4214 GstWebRTCKind caps_kind = webrtc_kind_from_caps (answer_caps);
4216 GST_WARNING_OBJECT (webrtc,
4217 "Trying to change kind of transceiver %" GST_PTR_FORMAT
4218 " at m-line %d from %s (%d) to %s (%d)", trans, rtp_trans->mline,
4219 gst_webrtc_kind_to_string (rtp_trans->kind), rtp_trans->kind,
4220 gst_webrtc_kind_to_string (caps_kind), caps_kind);
4223 answer_caps = gst_caps_make_writable (answer_caps);
4224 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
4225 GstStructure *s = gst_caps_get_structure (answer_caps, k);
4226 /* taken from the offer sdp already and already intersected above */
4227 gst_structure_remove_field (s, "a-mid");
4228 if (!trans->do_nack)
4229 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
4232 gst_sdp_media_set_media_from_caps (answer_caps, media);
4234 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
4237 original_target_pt = target_pt;
4239 _media_add_fec (media, trans, offer_caps, &target_pt);
4240 if (trans->do_nack) {
4241 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
4242 if (target_pt != original_target_pt)
4243 _media_add_rtx (media, trans, offer_caps, original_target_pt,
4247 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
4248 _media_add_ssrcs (media, answer_caps, webrtc,
4249 WEBRTC_TRANSCEIVER (rtp_trans));
4251 gst_caps_unref (answer_caps);
4254 /* set the new media direction */
4255 offer_dir = _get_direction_from_media (offer_media);
4256 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
4257 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4258 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
4259 "transceiver direction");
4260 gst_caps_unref (offer_caps);
4263 _media_replace_direction (media, answer_dir);
4265 if (!trans->stream) {
4266 TransportStream *item;
4269 _get_or_create_transport_stream (webrtc,
4270 bundled_mids ? bundle_idx : i, FALSE);
4271 webrtc_transceiver_set_transport (trans, item);
4275 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
4278 g_string_append_printf (bundled_mids, " %s", mid);
4281 /* set the a=fingerprint: for this transport */
4282 _add_fingerprint_to_media (trans->stream->transport, media);
4284 gst_caps_unref (offer_caps);
4286 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
4292 if (error && *error)
4293 GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
4295 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
4296 gst_sdp_media_free (media);
4297 gst_sdp_media_copy (offer_media, &media);
4298 gst_sdp_media_set_port_info (media, 0, 0);
4299 /* Clear error here as it is not propagated to the caller and the media
4300 * is just skipped, i.e. more iterations are going to happen. */
4301 g_clear_error (error);
4303 gst_sdp_message_add_media (ret, media);
4304 gst_sdp_media_free (media);
4308 gchar *mids = g_string_free (bundled_mids, FALSE);
4310 gst_sdp_message_add_attribute (ret, "group", mids);
4315 g_free (bundle_ufrag);
4318 g_free (bundle_pwd);
4320 /* FIXME: can we add not matched transceivers? */
4322 /* XXX: only true for the initial offerer */
4323 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
4326 g_strfreev (bundled);
4328 g_list_free (seen_transceivers);
4330 if (webrtc->priv->last_generated_offer)
4331 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4332 webrtc->priv->last_generated_offer = NULL;
4333 if (webrtc->priv->last_generated_answer)
4334 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4336 GstSDPMessage *copy;
4337 gst_sdp_message_copy (ret, ©);
4338 webrtc->priv->last_generated_answer =
4339 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
4347 GstStructure *options;
4348 GstWebRTCSDPType type;
4351 static GstStructure *
4352 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
4354 GstWebRTCSessionDescription *desc = NULL;
4355 GstSDPMessage *sdp = NULL;
4356 GstStructure *s = NULL;
4357 GError *error = NULL;
4359 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
4360 gst_webrtc_sdp_type_to_string (data->type), data->options);
4362 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
4363 sdp = _create_offer_task (webrtc, data->options, &error);
4364 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
4365 sdp = _create_answer_task (webrtc, data->options, &error);
4367 g_assert_not_reached ();
4372 desc = gst_webrtc_session_description_new (data->type, sdp);
4373 s = gst_structure_new ("application/x-gst-promise",
4374 gst_webrtc_sdp_type_to_string (data->type),
4375 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
4377 g_warn_if_fail (error != NULL);
4378 GST_WARNING_OBJECT (webrtc, "returning error: %s",
4379 error ? error->message : "Unknown");
4380 s = gst_structure_new ("application/x-gst-promise",
4381 "error", G_TYPE_ERROR, error, NULL);
4382 g_clear_error (&error);
4388 gst_webrtc_session_description_free (desc);
4394 _free_create_sdp_data (struct create_sdp *data)
4397 gst_structure_free (data->options);
4402 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
4403 const GstStructure * options, GstPromise * promise)
4405 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4408 data->options = gst_structure_copy (options);
4409 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
4411 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4412 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4414 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4415 "Could not create offer. webrtcbin is closed");
4416 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4417 "error", G_TYPE_ERROR, error, NULL);
4419 gst_promise_reply (promise, s);
4421 g_clear_error (&error);
4426 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
4427 const GstStructure * options, GstPromise * promise)
4429 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4432 data->options = gst_structure_copy (options);
4433 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
4435 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4436 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4438 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4439 "Could not create answer. webrtcbin is closed.");
4440 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4441 "error", G_TYPE_ERROR, error, NULL);
4443 gst_promise_reply (promise, s);
4445 g_clear_error (&error);
4449 static GstWebRTCBinPad *
4450 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
4451 GstWebRTCRTPTransceiver * trans, guint serial)
4453 GstWebRTCBinPad *pad;
4456 if (direction == GST_PAD_SINK) {
4457 if (serial == G_MAXUINT)
4458 serial = webrtc->priv->max_sink_pad_serial++;
4460 serial = webrtc->priv->src_pad_counter++;
4464 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
4466 pad = gst_webrtc_bin_pad_new (pad_name, direction);
4469 pad->trans = gst_object_ref (trans);
4474 static GstWebRTCRTPTransceiver *
4475 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
4476 const GstSDPMessage * sdp, guint media_idx)
4478 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4479 GstWebRTCRTPTransceiver *ret = NULL;
4482 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4483 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4485 if (g_strcmp0 (attr->key, "mid") == 0) {
4486 if ((ret = _find_transceiver_for_mid (webrtc, attr->value)))
4491 ret = _find_transceiver (webrtc, &media_idx,
4492 (FindTransceiverFunc) transceiver_match_for_mline);
4495 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
4500 _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
4502 GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4503 guint ulpfec_pt = 0, red_pt = 0;
4504 GstPad *sinkpad, *srcpad, *ghost;
4507 if (trans->stream) {
4509 transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
4510 red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
4513 if (trans->ulpfecenc || trans->redenc) {
4514 g_critical ("webrtcbin: duplicate call to create a fec encoder or "
4519 GST_DEBUG_OBJECT (webrtc,
4520 "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
4523 ret = gst_bin_new (NULL);
4525 trans->ulpfecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4526 gst_object_ref_sink (trans->ulpfecenc);
4527 if (!gst_bin_add (GST_BIN (ret), trans->ulpfecenc))
4528 g_warn_if_reached ();
4529 sinkpad = gst_element_get_static_pad (trans->ulpfecenc, "sink");
4531 g_object_bind_property (rtp_trans, "fec-percentage", trans->ulpfecenc,
4532 "percentage", G_BINDING_DEFAULT);
4534 trans->redenc = gst_element_factory_make ("rtpredenc", NULL);
4535 gst_object_ref_sink (trans->redenc);
4537 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
4538 rtp_trans->mline, red_pt);
4540 gst_bin_add (GST_BIN (ret), trans->redenc);
4541 gst_element_link (trans->ulpfecenc, trans->redenc);
4543 ghost = gst_ghost_pad_new ("sink", sinkpad);
4544 gst_clear_object (&sinkpad);
4545 gst_element_add_pad (ret, ghost);
4548 srcpad = gst_element_get_static_pad (trans->redenc, "src");
4549 ghost = gst_ghost_pad_new ("src", srcpad);
4550 gst_clear_object (&srcpad);
4551 gst_element_add_pad (ret, ghost);
4558 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
4560 GstStructure *s = user_data;
4562 gst_structure_id_set_value (s, field_id, value);
4567 #define GST_WEBRTC_PAYLOAD_TYPE "gst.webrtcbin.payload.type"
4570 try_match_transceiver_with_fec_decoder (GstWebRTCBin * webrtc,
4571 WebRTCTransceiver * trans)
4575 for (l = trans->stream->fecdecs; l; l = l->next) {
4576 GstElement *fecdec = GST_ELEMENT (l->data);
4577 gboolean found_transceiver = FALSE;
4582 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fecdec),
4583 GST_WEBRTC_PAYLOAD_TYPE));
4584 if (original_pt <= 0) {
4585 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
4586 "transceiver, fec decoder %" GST_PTR_FORMAT " does not contain a "
4587 "valid payload type", fecdec);
4591 for (i = 0; i < trans->stream->ptmap->len; i++) {
4592 PtMapItem *item = &g_array_index (trans->stream->ptmap, PtMapItem, i);
4594 /* FIXME: this only works for a 1-1 original_pt->fec_pt mapping */
4595 if (original_pt == item->pt && item->media_idx != -1
4596 && item->media_idx == trans->parent.mline) {
4597 if (trans->ulpfecdec) {
4598 GST_FIXME_OBJECT (trans, "cannot");
4599 gst_clear_object (&trans->ulpfecdec);
4601 trans->ulpfecdec = gst_object_ref (fecdec);
4602 found_transceiver = TRUE;
4607 if (!found_transceiver) {
4608 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
4615 _set_internal_rtpbin_element_props_from_stream (GstWebRTCBin * webrtc,
4616 TransportStream * stream)
4618 GstStructure *merged_local_rtx_ssrc_map;
4619 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4620 GValue red_pt_array = { 0, };
4625 gst_value_array_init (&red_pt_array, 0);
4627 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4628 GST_DEBUG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4630 for (i = 0; i < rtx_count; i++) {
4631 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4632 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4633 const gchar *apt = gst_structure_get_string (s, "apt");
4635 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4636 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4639 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4640 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4641 stream->rtxsend, pt_map);
4643 if (stream->rtxreceive)
4644 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4645 if (stream->rtxsend)
4646 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4648 gst_structure_free (pt_map);
4649 g_clear_pointer (&rtx_pt, g_free);
4651 merged_local_rtx_ssrc_map =
4652 gst_structure_new_empty ("application/x-rtp-ssrc-map");
4654 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4655 GstWebRTCRTPTransceiver *rtp_trans =
4656 g_ptr_array_index (webrtc->priv->transceivers, i);
4657 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4659 if (trans->stream == stream) {
4660 gint ulpfec_pt, red_pt = 0;
4662 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC", rtp_trans->mline);
4666 red_pt = transport_stream_get_pt (stream, "RED", rtp_trans->mline);
4670 GValue ptval = { 0, };
4672 g_value_init (&ptval, G_TYPE_INT);
4673 g_value_set_int (&ptval, red_pt);
4674 gst_value_array_append_value (&red_pt_array, &ptval);
4675 g_value_unset (&ptval);
4678 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " transceiever %"
4679 GST_PTR_FORMAT " has FEC payload %d and RED payload %d", stream,
4680 trans, ulpfec_pt, red_pt);
4682 if (trans->ulpfecenc) {
4683 guint ulpfecenc_pt = ulpfec_pt;
4685 if (ulpfecenc_pt == 0)
4688 g_object_set (trans->ulpfecenc, "pt", ulpfecenc_pt, "multipacket",
4689 rtp_trans->kind == GST_WEBRTC_KIND_VIDEO, "percentage",
4690 trans->fec_percentage, NULL);
4693 try_match_transceiver_with_fec_decoder (webrtc, trans);
4694 if (trans->ulpfecdec) {
4695 g_object_set (trans->ulpfecdec, "pt", ulpfec_pt, NULL);
4698 if (trans->redenc) {
4699 gboolean always_produce = TRUE;
4701 /* passthrough settings */
4703 always_produce = FALSE;
4705 g_object_set (trans->redenc, "pt", red_pt, "allow-no-red-blocks",
4706 always_produce, NULL);
4709 if (trans->local_rtx_ssrc_map) {
4710 gst_structure_foreach (trans->local_rtx_ssrc_map,
4711 _merge_structure, merged_local_rtx_ssrc_map);
4716 if (stream->rtxsend)
4717 g_object_set (stream->rtxsend, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
4718 gst_clear_structure (&merged_local_rtx_ssrc_map);
4720 if (stream->reddec) {
4721 g_object_set_property (G_OBJECT (stream->reddec), "payloads",
4725 g_value_unset (&red_pt_array);
4729 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
4734 * ,--------------------------------------------webrtcbin--------------------------------------------,
4736 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
4737 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
4738 * ; ,---clocksync---, ; ; ; ; ;
4739 * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
4740 * ; sink_%u ; ; ,---fec encoder---, ; ; '---------------------' ;
4741 * o---------o sink src o-o sink src o--o send_rtp_sink_%u ; ;
4742 * ; '---------------' ,-----------------, '--------------------' ;
4743 * '-------------------------------------------------------------------------------------------------'
4748 * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
4750 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
4751 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
4753 * ; sink_%u ,---clocksync---, ,---fec encoder---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
4754 * o----------o sink src o-o sink src o--o sink_%u ; ; ; '---------------------' ;
4755 * ; '---------------' ,-----------------, ; ; ; ; ;
4756 * ; ; src o-o send_rtp_sink_%u ; ;
4757 * ; sink_%u ,---clocksync---, ,---fec encoder---, ; ; ; ; ;
4758 * o----------o sink src o-o sink src o--o sink%u ; '--------------------' ;
4759 * ; '---------------' ,-----------------, '------------' ;
4760 * '-----------------------------------------------------------------------------------------------------------------'
4762 GstPadTemplate *rtp_templ;
4763 GstPad *rtp_sink, *sinkpad, *srcpad;
4765 WebRTCTransceiver *trans;
4766 GstElement *clocksync;
4767 GstElement *fec_encoder;
4769 g_return_val_if_fail (pad->trans != NULL, NULL);
4771 trans = WEBRTC_TRANSCEIVER (pad->trans);
4773 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
4775 g_assert (trans->stream);
4777 clocksync = gst_element_factory_make ("clocksync", NULL);
4778 g_object_set (clocksync, "sync", TRUE, NULL);
4779 gst_bin_add (GST_BIN (webrtc), clocksync);
4780 gst_element_sync_state_with_parent (clocksync);
4782 srcpad = gst_element_get_static_pad (clocksync, "src");
4784 fec_encoder = _build_fec_encoder (webrtc, trans);
4786 g_warn_if_reached ();
4790 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
4792 gst_bin_add (GST_BIN (webrtc), fec_encoder);
4793 gst_element_sync_state_with_parent (fec_encoder);
4795 sinkpad = gst_element_get_static_pad (fec_encoder, "sink");
4796 if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)
4797 g_warn_if_reached ();
4798 gst_clear_object (&srcpad);
4799 gst_clear_object (&sinkpad);
4800 sinkpad = gst_element_get_static_pad (clocksync, "sink");
4801 srcpad = gst_element_get_static_pad (fec_encoder, "src");
4803 if (!webrtc->rtpfunnel) {
4805 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
4806 "send_rtp_sink_%u");
4807 g_assert (rtp_templ);
4809 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
4811 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
4813 gst_pad_link (srcpad, rtp_sink);
4814 gst_object_unref (rtp_sink);
4816 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
4817 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4818 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
4819 g_warn_if_reached ();
4822 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
4823 GstPad *funnel_sinkpad =
4824 gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
4826 gst_pad_link (srcpad, funnel_sinkpad);
4829 gst_object_unref (funnel_sinkpad);
4832 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
4834 gst_clear_object (&srcpad);
4835 gst_clear_object (&sinkpad);
4837 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
4839 return GST_PAD (pad);
4842 /* output pads are receiving elements */
4844 _connect_output_stream (GstWebRTCBin * webrtc,
4845 TransportStream * stream, guint session_id)
4848 * ,------------------------webrtcbin------------------------,
4849 * ; ,---------rtpbin---------, ;
4850 * ; ,-transport_receive_%u--, ; ; ;
4851 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
4853 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
4854 * ; '-----------------------' ; ; ; src_%u
4855 * ; ; recv_rtp_src_%u_%u_%u o--o
4856 * ; '------------------------' ;
4857 * '---------------------------------------------------------'
4861 if (stream->output_connected) {
4862 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
4863 "connected to rtpbin. Not connecting", stream);
4867 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
4868 session_id, stream);
4870 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
4871 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
4872 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
4873 g_warn_if_reached ();
4876 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
4878 /* The webrtcbin src_%u output pads will be created when rtpbin receives
4879 * data on that stream in on_rtpbin_pad_added() */
4881 stream->output_connected = TRUE;
4891 _clear_ice_candidate_item (IceCandidateItem * item)
4893 g_free (item->candidate);
4897 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
4898 gboolean drop_invalid)
4900 GstWebRTCICEStream *stream;
4902 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
4903 if (stream == NULL) {
4905 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
4908 IceCandidateItem new;
4909 new.mlineindex = item->mlineindex;
4910 new.candidate = g_strdup (item->candidate);
4911 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
4914 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
4915 ICE_UNLOCK (webrtc);
4920 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4921 item->mlineindex, item->candidate);
4923 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
4927 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
4928 const GstSDPMedia * media)
4931 GstWebRTCICEStream *stream = NULL;
4933 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
4934 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
4935 if (g_strcmp0 (attr->key, "candidate") == 0) {
4939 stream = _find_ice_stream_for_session (webrtc, mlineindex);
4940 if (stream == NULL) {
4941 GST_DEBUG_OBJECT (webrtc,
4942 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
4946 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
4947 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4948 mlineindex, candidate);
4949 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
4956 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
4957 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
4959 GstSDPMedia *media = NULL;
4961 if (mline_index < sdp->medias->len) {
4962 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
4965 if (media == NULL) {
4966 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
4970 // Add the candidate as an attribute, first stripping off the existing
4971 // candidate: key from the string description
4972 if (strlen (candidate) < 10) {
4973 GST_WARNING_OBJECT (webrtc,
4974 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
4978 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
4982 _filter_sdp_fields (GQuark field_id, const GValue * value,
4983 GstStructure * new_structure)
4985 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
4986 gst_structure_id_set_value (new_structure, field_id, value);
4992 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
4993 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
4997 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5000 proto = gst_sdp_media_get_proto (media);
5001 if (proto != NULL) {
5002 /* Parse global SDP attributes once */
5003 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
5004 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
5005 gst_sdp_message_attributes_to_caps (sdp, global_caps);
5006 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
5007 gst_sdp_media_attributes_to_caps (media, global_caps);
5009 len = gst_sdp_media_formats_len (media);
5010 for (i = 0; i < len; i++) {
5011 GstCaps *caps, *outcaps;
5017 pt = atoi (gst_sdp_media_get_format (media, i));
5019 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
5022 caps = gst_sdp_media_get_caps_from_media (media, pt);
5024 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
5028 /* Merge in global caps */
5029 /* Intersect will merge in missing fields to the current caps */
5030 outcaps = gst_caps_intersect (caps, global_caps);
5031 gst_caps_unref (caps);
5033 s = gst_caps_get_structure (outcaps, 0);
5034 gst_structure_set_name (s, "application/x-rtp");
5035 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
5036 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
5038 item.caps = gst_caps_new_empty ();
5040 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
5041 GstStructure *s = gst_caps_get_structure (outcaps, j);
5042 GstStructure *filtered =
5043 gst_structure_new_empty (gst_structure_get_name (s));
5045 gst_structure_foreach (s,
5046 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
5047 gst_caps_append_structure (item.caps, filtered);
5051 item.media_idx = media_idx;
5052 gst_caps_unref (outcaps);
5054 g_array_append_val (stream->ptmap, item);
5057 gst_caps_unref (global_caps);
5062 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
5063 const GstSDPMessage * sdp, guint media_idx,
5064 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
5065 GStrv bundled, guint bundle_idx, GError ** error)
5067 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
5068 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
5069 GstWebRTCRTPTransceiverDirection new_dir;
5070 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5071 GstWebRTCDTLSSetup new_setup;
5072 gboolean new_rtcp_rsize;
5073 ReceiveState receive_state = RECEIVE_STATE_UNSET;
5076 rtp_trans->mline = media_idx;
5078 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
5079 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
5080 GST_FIXME_OBJECT (webrtc, "Updating video transceiver %" GST_PTR_FORMAT
5081 " to audio, which isn't fully supported.", rtp_trans);
5082 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
5085 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
5086 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
5087 GST_FIXME_OBJECT (webrtc, "Updating audio transceiver %" GST_PTR_FORMAT
5088 " to video, which isn't fully supported.", rtp_trans);
5089 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
5092 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
5093 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
5095 if (g_strcmp0 (attr->key, "mid") == 0) {
5096 g_free (rtp_trans->mid);
5097 rtp_trans->mid = g_strdup (attr->value);
5102 const GstSDPMedia *local_media, *remote_media;
5103 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
5104 GstWebRTCDTLSSetup local_setup, remote_setup;
5107 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5110 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5113 local_setup = _get_dtls_setup_from_media (local_media);
5114 remote_setup = _get_dtls_setup_from_media (remote_media);
5115 new_setup = _get_final_setup (local_setup, remote_setup);
5116 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5117 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5118 "Cannot intersect direction attributes for media %u", media_idx);
5122 local_dir = _get_direction_from_media (local_media);
5123 remote_dir = _get_direction_from_media (remote_media);
5124 new_dir = _get_final_direction (local_dir, remote_dir);
5125 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
5126 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5127 "Cannot intersect dtls setup attributes for media %u", media_idx);
5131 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
5132 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
5133 && prev_dir != new_dir) {
5134 g_set_error (error, GST_WEBRTC_ERROR,
5135 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5136 "transceiver direction changes are not implemented. Media %u",
5141 if (!bundled || bundle_idx == media_idx) {
5142 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
5143 && _media_has_attribute_key (remote_media, "rtcp-rsize");
5147 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
5148 media_idx, &session);
5150 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
5151 g_object_unref (session);
5157 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5159 /* Not a bundled stream means this entire transport is inactive,
5160 * so set the receive state to BLOCK below */
5161 stream->active = FALSE;
5162 receive_state = RECEIVE_STATE_BLOCK;
5165 /* If this transceiver is active for sending or receiving,
5166 * we still need receive at least RTCP, so need to unblock
5167 * the receive bin below. */
5168 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
5169 receive_state = RECEIVE_STATE_PASS;
5170 stream->active = TRUE;
5173 if (new_dir != prev_dir) {
5174 gchar *prev_dir_s, *new_dir_s;
5175 guint rtp_session_id = bundled ? bundle_idx : media_idx;
5178 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
5181 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
5184 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
5185 " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
5187 g_free (prev_dir_s);
5192 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5193 GstWebRTCBinPad *pad;
5195 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
5197 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
5199 GstPad *peer = gst_pad_get_peer (target);
5201 gst_pad_send_event (peer, gst_event_new_eos ());
5202 gst_object_unref (peer);
5204 gst_object_unref (target);
5206 gst_object_unref (pad);
5209 /* XXX: send eos event up the sink pad as well? */
5212 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
5213 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5214 GstWebRTCBinPad *pad =
5215 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
5218 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
5219 " for transceiver %" GST_PTR_FORMAT, pad, trans);
5220 gst_object_unref (pad);
5222 GST_DEBUG_OBJECT (webrtc,
5223 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
5224 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
5226 _connect_input_stream (webrtc, pad);
5227 _add_pad (webrtc, pad);
5230 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
5231 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5232 GstWebRTCBinPad *pad =
5233 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5235 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
5236 " for transceiver %" GST_PTR_FORMAT, pad, trans);
5237 gst_object_unref (pad);
5239 GST_DEBUG_OBJECT (webrtc,
5240 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
5241 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
5244 if (!trans->stream) {
5245 TransportStream *item;
5248 _get_or_create_transport_stream (webrtc, rtp_session_id, FALSE);
5249 webrtc_transceiver_set_transport (trans, item);
5252 _connect_output_stream (webrtc, trans->stream, rtp_session_id);
5253 /* delay adding the pad until rtpbin creates the recv output pad
5254 * to ghost to so queries/events travel through the pipeline correctly
5255 * as soon as the pad is added */
5256 _add_pad_to_list (webrtc, pad);
5261 rtp_trans->mline = media_idx;
5262 rtp_trans->current_direction = new_dir;
5265 if (!bundled || bundle_idx == media_idx) {
5266 if (stream->rtxsend || stream->rtxreceive) {
5267 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
5270 g_object_set (stream, "dtls-client",
5271 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5274 /* Must be after setting the "dtls-client" so that data is not pushed into
5275 * the dtlssrtp elements before the ssl direction has been set which will
5276 * throw SSL errors */
5277 if (receive_state != RECEIVE_STATE_UNSET)
5278 transport_receive_bin_set_receive_state (stream->receive_bin,
5282 /* must be called with the pc lock held */
5284 _generate_data_channel_id (GstWebRTCBin * webrtc)
5287 gint new_id = -1, max_channels = 0;
5289 if (webrtc->priv->sctp_transport) {
5290 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5293 if (max_channels <= 0) {
5294 max_channels = 65534;
5297 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
5300 /* TODO: a better search algorithm */
5302 WebRTCDataChannel *channel;
5306 if (new_id < 0 || new_id >= max_channels) {
5307 /* exhausted id space */
5308 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
5309 "data channel id (max %i)", max_channels);
5313 /* client must generate even ids, server must generate odd ids */
5314 if (new_id % 2 == ! !is_client)
5317 channel = _find_data_channel_for_id (webrtc, new_id);
5326 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
5327 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
5330 const GstSDPMedia *local_media, *remote_media;
5331 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
5332 TransportReceiveBin *receive;
5333 int local_port, remote_port;
5334 guint64 local_max_size, remote_max_size, max_size;
5338 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5341 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5344 local_setup = _get_dtls_setup_from_media (local_media);
5345 remote_setup = _get_dtls_setup_from_media (remote_media);
5346 new_setup = _get_final_setup (local_setup, remote_setup);
5347 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5348 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5349 "Cannot intersect dtls setup for media %u", media_idx);
5353 /* data channel is always rtcp-muxed to avoid generating ICE candidates
5355 g_object_set (stream, "dtls-client",
5356 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5358 local_port = _get_sctp_port_from_media (local_media);
5359 remote_port = _get_sctp_port_from_media (local_media);
5360 if (local_port == -1 || remote_port == -1) {
5361 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5362 "Could not find sctp port for media %u (local %i, remote %i)",
5363 media_idx, local_port, remote_port);
5367 if (0 == (local_max_size =
5368 _get_sctp_max_message_size_from_media (local_media)))
5369 local_max_size = G_MAXUINT64;
5370 if (0 == (remote_max_size =
5371 _get_sctp_max_message_size_from_media (remote_media)))
5372 remote_max_size = G_MAXUINT64;
5373 max_size = MIN (local_max_size, remote_max_size);
5375 webrtc->priv->sctp_transport->max_message_size = max_size;
5378 guint orig_local_port, orig_remote_port;
5380 /* XXX: sctpassociation warns if we are in the wrong state */
5381 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5382 &orig_local_port, NULL);
5384 if (orig_local_port != local_port)
5385 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5388 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5389 &orig_remote_port, NULL);
5390 if (orig_remote_port != remote_port)
5391 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5396 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
5397 WebRTCDataChannel *channel;
5399 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
5401 if (channel->parent.id == -1)
5402 channel->parent.id = _generate_data_channel_id (webrtc);
5403 if (channel->parent.id == -1)
5404 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5405 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5407 if (webrtc->priv->sctp_transport->association_established
5408 && !channel->parent.negotiated && !channel->opened) {
5409 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
5410 webrtc_data_channel_start_negotiation (channel);
5415 stream->active = TRUE;
5417 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
5418 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
5422 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
5425 GstWebRTCKind kind = GPOINTER_TO_INT (data);
5429 if (p1->mline != -1)
5433 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
5440 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
5443 GstPad *queue_srcpad;
5445 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
5450 if (webrtc->rtpfunnel)
5453 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
5454 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
5455 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
5457 queue = gst_element_factory_make ("queue", NULL);
5458 gst_bin_add (GST_BIN (webrtc), queue);
5459 gst_element_sync_state_with_parent (queue);
5461 gst_element_link (webrtc->rtpfunnel, queue);
5463 queue_srcpad = gst_element_get_static_pad (queue, "src");
5465 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
5466 rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
5468 gst_pad_link (queue_srcpad, rtp_sink);
5469 gst_object_unref (queue_srcpad);
5470 gst_object_unref (rtp_sink);
5472 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
5473 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5474 GST_ELEMENT (stream->send_bin), "rtp_sink"))
5475 g_warn_if_reached ();
5483 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
5484 GstWebRTCSessionDescription * sdp, GError ** error)
5487 gboolean ret = FALSE;
5488 GStrv bundled = NULL;
5489 guint bundle_idx = 0;
5490 TransportStream *bundle_stream = NULL;
5492 /* FIXME: With some peers, it's possible we could have
5493 * multiple bundles to deal with, although I've never seen one yet */
5494 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5495 if (!_parse_bundle (sdp->sdp, &bundled, error))
5500 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
5501 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5502 "Bundle tag is %s but no media found matching", bundled[0]);
5506 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
5507 _message_media_is_datachannel (sdp->sdp, bundle_idx));
5508 /* Mark the bundle stream as inactive to start. It will be set to TRUE
5509 * by any bundled mline that is active, and at the end we set the
5510 * receivebin to BLOCK if all mlines were inactive. */
5511 bundle_stream->active = FALSE;
5513 g_array_set_size (bundle_stream->ptmap, 0);
5514 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5515 /* When bundling, we need to do this up front, or else RTX
5516 * parameters aren't set up properly for the bundled streams */
5517 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
5520 _connect_rtpfunnel (webrtc, bundle_idx);
5523 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5524 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5525 TransportStream *stream;
5526 GstWebRTCRTPTransceiver *trans;
5527 guint transport_idx;
5529 /* skip rejected media */
5530 if (gst_sdp_media_get_port (media) == 0)
5534 transport_idx = bundle_idx;
5538 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5540 stream = _get_or_create_transport_stream (webrtc, transport_idx,
5541 _message_media_is_datachannel (sdp->sdp, transport_idx));
5543 /* When bundling, these were all set up above, but when not
5544 * bundling we need to do it now */
5545 g_array_set_size (stream->ptmap, 0);
5546 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
5550 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
5552 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
5553 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5554 "State mismatch. Could not find local transceiver by mline %u", i);
5557 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
5558 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
5559 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
5561 /* No existing transceiver, find an unused one */
5563 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
5564 kind = GST_WEBRTC_KIND_AUDIO;
5565 else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
5566 kind = GST_WEBRTC_KIND_VIDEO;
5568 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
5569 GST_STR_NULL (gst_sdp_media_get_media (media)));
5571 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
5572 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
5575 /* Still no transceiver? Create one */
5576 /* XXX: default to the advertised direction in the sdp for new
5577 * transceivers. The spec doesn't actually say what happens here, only
5578 * that calls to setDirection will change the value. Nothing about
5579 * a default value when the transceiver is created internally */
5581 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
5582 _get_direction_from_media (media), i, kind, NULL);
5583 webrtc_transceiver_set_transport (t, stream);
5584 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
5587 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
5588 trans, bundled, bundle_idx, error);
5589 if (error && *error)
5591 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
5592 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
5594 if (error && *error)
5597 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
5602 if (bundle_stream && bundle_stream->active == FALSE) {
5603 /* No bundled mline marked the bundle as active, so block the receive bin, as
5604 * this bundle is completely inactive */
5605 GST_LOG_OBJECT (webrtc,
5606 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
5607 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
5608 RECEIVE_STATE_BLOCK);
5614 g_strfreev (bundled);
5620 check_transceivers_not_removed (GstWebRTCBin * webrtc,
5621 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
5626 if (gst_sdp_message_medias_len (previous->sdp) >
5627 gst_sdp_message_medias_len (new->sdp))
5634 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
5639 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5640 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5641 GstWebRTCRTPTransceiver *rtp_trans;
5642 WebRTCTransceiver *trans;
5644 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5645 /* only look for matching mid */
5646 if (rtp_trans == NULL)
5649 trans = WEBRTC_TRANSCEIVER (rtp_trans);
5651 /* We only validate the locked mlines for now */
5652 if (!trans->mline_locked)
5655 if (rtp_trans->mline != i) {
5656 g_set_error (error, GST_WEBRTC_ERROR,
5657 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5658 "m-line with mid %s is at position %d, but was locked to %d, "
5659 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
5663 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
5664 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
5665 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
5666 char *trans_kind = gst_webrtc_kind_to_string (rtp_trans->kind);
5667 g_set_error (error, GST_WEBRTC_ERROR,
5668 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5669 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
5670 "%s media", i, GST_OBJECT_NAME (rtp_trans), trans_kind,
5671 gst_sdp_media_get_media (media));
5672 g_free (trans_kind);
5676 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
5677 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
5678 char *trans_kind = gst_webrtc_kind_to_string (rtp_trans->kind);
5679 g_set_error (error, GST_WEBRTC_ERROR,
5680 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5681 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
5682 "%s media", i, GST_OBJECT_NAME (rtp_trans), trans_kind,
5683 gst_sdp_media_get_media (media));
5684 g_free (trans_kind);
5694 struct set_description
5697 GstWebRTCSessionDescription *sdp;
5700 static GstWebRTCSessionDescription *
5701 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
5702 GstWebRTCSDPType type)
5705 case GST_WEBRTC_SDP_TYPE_OFFER:
5706 case GST_WEBRTC_SDP_TYPE_PRANSWER:
5707 case GST_WEBRTC_SDP_TYPE_ANSWER:
5708 if (source == SDP_LOCAL) {
5709 return webrtc->current_local_description;
5711 return webrtc->current_remote_description;
5713 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
5716 /* other values mean memory corruption/uninitialized! */
5717 g_assert_not_reached ();
5724 /* http://w3c.github.io/webrtc-pc/#set-description */
5725 static GstStructure *
5726 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
5728 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
5729 gboolean signalling_state_changed = FALSE;
5730 GError *error = NULL;
5731 GStrv bundled = NULL;
5732 guint bundle_idx = 0;
5736 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5737 webrtc->signaling_state);
5739 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
5740 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
5741 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
5742 _sdp_source_to_string (sd->source), type_str, state);
5743 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
5749 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
5752 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5753 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
5757 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
5758 g_set_error (&error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5759 "Bundle tag is %s but no matching media found", bundled[0]);
5764 if (!check_transceivers_not_removed (webrtc,
5765 get_previous_description (webrtc, sd->source, sd->sdp->type),
5767 g_set_error_literal (&error, GST_WEBRTC_ERROR,
5768 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5769 "m=lines removed from the SDP. Processing a completely new connection "
5770 "is not currently supported.");
5774 if (!check_locked_mlines (webrtc, sd->sdp, &error))
5777 switch (sd->sdp->type) {
5778 case GST_WEBRTC_SDP_TYPE_OFFER:{
5779 if (sd->source == SDP_LOCAL) {
5780 if (webrtc->pending_local_description)
5781 gst_webrtc_session_description_free
5782 (webrtc->pending_local_description);
5783 webrtc->pending_local_description =
5784 gst_webrtc_session_description_copy (sd->sdp);
5785 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
5787 if (webrtc->pending_remote_description)
5788 gst_webrtc_session_description_free
5789 (webrtc->pending_remote_description);
5790 webrtc->pending_remote_description =
5791 gst_webrtc_session_description_copy (sd->sdp);
5792 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
5796 case GST_WEBRTC_SDP_TYPE_ANSWER:{
5797 if (sd->source == SDP_LOCAL) {
5798 if (webrtc->current_local_description)
5799 gst_webrtc_session_description_free
5800 (webrtc->current_local_description);
5801 webrtc->current_local_description =
5802 gst_webrtc_session_description_copy (sd->sdp);
5804 if (webrtc->current_remote_description)
5805 gst_webrtc_session_description_free
5806 (webrtc->current_remote_description);
5807 webrtc->current_remote_description = webrtc->pending_remote_description;
5808 webrtc->pending_remote_description = NULL;
5810 if (webrtc->current_remote_description)
5811 gst_webrtc_session_description_free
5812 (webrtc->current_remote_description);
5813 webrtc->current_remote_description =
5814 gst_webrtc_session_description_copy (sd->sdp);
5816 if (webrtc->current_local_description)
5817 gst_webrtc_session_description_free
5818 (webrtc->current_local_description);
5819 webrtc->current_local_description = webrtc->pending_local_description;
5820 webrtc->pending_local_description = NULL;
5823 if (webrtc->pending_local_description)
5824 gst_webrtc_session_description_free (webrtc->pending_local_description);
5825 webrtc->pending_local_description = NULL;
5827 if (webrtc->pending_remote_description)
5828 gst_webrtc_session_description_free
5829 (webrtc->pending_remote_description);
5830 webrtc->pending_remote_description = NULL;
5832 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5835 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
5836 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
5837 if (sd->source == SDP_LOCAL) {
5838 if (webrtc->pending_local_description)
5839 gst_webrtc_session_description_free
5840 (webrtc->pending_local_description);
5841 webrtc->pending_local_description = NULL;
5843 if (webrtc->pending_remote_description)
5844 gst_webrtc_session_description_free
5845 (webrtc->pending_remote_description);
5846 webrtc->pending_remote_description = NULL;
5849 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5852 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
5853 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
5854 if (sd->source == SDP_LOCAL) {
5855 if (webrtc->pending_local_description)
5856 gst_webrtc_session_description_free
5857 (webrtc->pending_local_description);
5858 webrtc->pending_local_description =
5859 gst_webrtc_session_description_copy (sd->sdp);
5861 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
5863 if (webrtc->pending_remote_description)
5864 gst_webrtc_session_description_free
5865 (webrtc->pending_remote_description);
5866 webrtc->pending_remote_description =
5867 gst_webrtc_session_description_copy (sd->sdp);
5869 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
5875 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
5877 * If the mid value of an RTCRtpTransceiver was set to a non-null value
5878 * by the RTCSessionDescription that is being rolled back, set the mid
5879 * value of that transceiver to null, as described by [JSEP]
5880 * (section 4.1.7.2.).
5881 * If an RTCRtpTransceiver was created by applying the
5882 * RTCSessionDescription that is being rolled back, and a track has not
5883 * been attached to it via addTrack, remove that transceiver from
5884 * connection's set of transceivers, as described by [JSEP]
5885 * (section 4.1.7.2.).
5886 * Restore the value of connection's [[ sctpTransport]] internal slot
5887 * to its value at the last stable signaling state.
5891 if (webrtc->signaling_state != new_signaling_state) {
5892 webrtc->signaling_state = new_signaling_state;
5893 signalling_state_changed = TRUE;
5897 gboolean ice_controller = FALSE;
5899 /* get the current value so we don't change ice controller from TRUE to
5900 * FALSE on renegotiation or once set to TRUE for the initial local offer */
5901 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
5903 /* we control ice negotiation if we send the initial offer */
5905 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
5906 && webrtc->current_remote_description == NULL;
5907 /* or, if the remote is an ice-lite peer */
5908 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
5909 && webrtc->current_remote_description
5910 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
5913 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
5914 ice_controller ? "true" : "false");
5915 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
5918 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5921 /* media modifications */
5922 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
5925 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
5926 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
5927 GstWebRTCRTPTransceiverDirection new_dir;
5929 const GstSDPMedia *media;
5931 if (!pad->received_caps) {
5932 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
5937 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
5938 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
5943 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
5944 /* skip rejected media */
5945 if (gst_sdp_media_get_port (media) == 0) {
5946 /* FIXME: arrange for an appropriate flow return */
5947 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
5948 "a more correct flow return.");
5954 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
5959 new_dir = pad->trans->direction;
5960 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
5961 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5962 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
5963 "data at the moment. Not connecting input stream yet", pad->trans);
5968 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
5969 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
5970 pad->trans, pad->received_caps);
5971 _connect_input_stream (webrtc, pad);
5972 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
5976 gst_object_unref (old->data);
5977 webrtc->priv->pending_sink_transceivers =
5978 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
5982 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
5983 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
5985 TransportStream *item;
5986 guint rtp_session_id = bundled ? bundle_idx : i;
5989 _get_or_create_transport_stream (webrtc, rtp_session_id,
5990 _message_media_is_datachannel (sd->sdp->sdp, rtp_session_id));
5992 if (sd->source == SDP_REMOTE) {
5995 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
5996 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
5998 if (g_strcmp0 (attr->key, "ssrc") == 0) {
5999 GStrv split = g_strsplit (attr->value, " ", 0);
6002 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
6003 && g_str_has_prefix (split[1], "cname:")) {
6004 g_ptr_array_add (item->remote_ssrcmap, ssrcmap_item_new (ssrc, i));
6011 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
6012 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6014 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
6015 item->stream, ufrag, pwd);
6018 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
6019 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6021 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
6022 item->stream, ufrag, pwd);
6028 if (sd->source == SDP_LOCAL) {
6029 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
6030 IceStreamItem *item =
6031 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
6033 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
6037 /* Add any pending trickle ICE candidates if we have both offer and answer */
6038 if (webrtc->current_local_description && webrtc->current_remote_description) {
6041 GstWebRTCSessionDescription *remote_sdp =
6042 webrtc->current_remote_description;
6044 /* Add any remote ICE candidates from the remote description to
6045 * support non-trickle peers first */
6046 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
6047 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
6048 _add_ice_candidates_from_sdp (webrtc, i, media);
6052 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
6053 IceCandidateItem *item =
6054 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
6055 IceCandidateItem, i);
6057 _add_ice_candidate (webrtc, item, TRUE);
6059 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
6060 ICE_UNLOCK (webrtc);
6064 * If connection's signaling state changed above, fire an event named
6065 * signalingstatechange at connection.
6067 if (signalling_state_changed) {
6068 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6069 webrtc->signaling_state);
6070 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6071 new_signaling_state);
6072 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
6075 g_object_notify (G_OBJECT (webrtc), "signaling-state");
6082 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6083 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
6085 /* If connection's signaling state is now stable, update the
6086 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
6087 * was true both before and after this update, queue a task to check
6088 * connection's [[needNegotiation]] slot and, if still true, fire a
6089 * simple event named negotiationneeded at connection.*/
6090 _update_need_negotiation (webrtc);
6091 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
6092 _check_need_negotiation_task (webrtc, NULL);
6097 g_strfreev (bundled);
6100 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6101 "error", G_TYPE_ERROR, error, NULL);
6102 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
6103 g_clear_error (&error);
6111 _free_set_description_data (struct set_description *sd)
6114 gst_webrtc_session_description_free (sd->sdp);
6119 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
6120 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
6122 struct set_description *sd;
6124 if (remote_sdp == NULL)
6126 if (remote_sdp->sdp == NULL)
6129 sd = g_new0 (struct set_description, 1);
6130 sd->source = SDP_REMOTE;
6131 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
6133 if (!gst_webrtc_bin_enqueue_task (webrtc,
6134 (GstWebRTCBinFunc) _set_description_task, sd,
6135 (GDestroyNotify) _free_set_description_data, promise)) {
6137 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6138 "Could not set remote description. webrtcbin is closed.");
6139 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6140 "error", G_TYPE_ERROR, error, NULL);
6142 gst_promise_reply (promise, s);
6144 g_clear_error (&error);
6151 gst_promise_reply (promise, NULL);
6152 g_return_if_reached ();
6157 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
6158 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
6160 struct set_description *sd;
6162 if (local_sdp == NULL)
6164 if (local_sdp->sdp == NULL)
6167 sd = g_new0 (struct set_description, 1);
6168 sd->source = SDP_LOCAL;
6169 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
6171 if (!gst_webrtc_bin_enqueue_task (webrtc,
6172 (GstWebRTCBinFunc) _set_description_task, sd,
6173 (GDestroyNotify) _free_set_description_data, promise)) {
6175 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6176 "Could not set local description. webrtcbin is closed");
6177 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6178 "error", G_TYPE_ERROR, error, NULL);
6180 gst_promise_reply (promise, s);
6182 g_clear_error (&error);
6189 gst_promise_reply (promise, NULL);
6190 g_return_if_reached ();
6194 static GstStructure *
6195 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
6197 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
6198 IceCandidateItem new;
6199 new.mlineindex = item->mlineindex;
6200 new.candidate = g_steal_pointer (&item->candidate);
6203 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
6204 ICE_UNLOCK (webrtc);
6206 _add_ice_candidate (webrtc, item, FALSE);
6213 _free_ice_candidate_item (IceCandidateItem * item)
6215 _clear_ice_candidate_item (item);
6220 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
6223 IceCandidateItem *item;
6225 item = g_new0 (IceCandidateItem, 1);
6226 item->mlineindex = mline;
6227 if (attr && attr[0] != 0) {
6228 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
6229 item->candidate = g_strdup (attr);
6230 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
6231 item->candidate = g_strdup_printf ("a=%s", attr);
6233 gst_webrtc_bin_enqueue_task (webrtc,
6234 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
6235 (GDestroyNotify) _free_ice_candidate_item, NULL);
6238 static GstStructure *
6239 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
6245 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
6246 ICE_UNLOCK (webrtc);
6247 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
6248 return NULL; /* Nothing to process */
6250 /* Take the array so we can process it all and free it later
6251 * without holding the lock
6252 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
6254 items = webrtc->priv->pending_local_ice_candidates;
6255 /* Replace with a new array */
6256 webrtc->priv->pending_local_ice_candidates =
6257 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
6258 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
6259 (GDestroyNotify) _clear_ice_candidate_item);
6260 ICE_UNLOCK (webrtc);
6262 for (i = 0; i < items->len; i++) {
6263 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
6264 const gchar *cand = item->candidate;
6266 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
6267 /* stripping away "a=" */
6271 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
6272 item->mlineindex, cand);
6274 /* First, merge this ice candidate into the appropriate mline
6275 * in the local-description SDP.
6276 * Second, emit the on-ice-candidate signal for the app.
6278 * FIXME: This ICE candidate should be stored somewhere with
6279 * the associated mid and also merged back into any subsequent
6280 * local descriptions on renegotiation */
6281 if (webrtc->current_local_description)
6282 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
6283 item->mlineindex, cand);
6284 if (webrtc->pending_local_description)
6285 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
6286 item->mlineindex, cand);
6289 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
6290 0, item->mlineindex, cand);
6294 g_array_free (items, TRUE);
6300 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
6301 gchar * candidate, GstWebRTCBin * webrtc)
6303 IceCandidateItem item;
6304 gboolean queue_task = FALSE;
6306 item.mlineindex = session_id;
6307 item.candidate = g_strdup (candidate);
6310 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
6312 /* Let the first pending candidate queue a task each time, which will
6313 * handle any that arrive between now and when the task runs */
6314 if (webrtc->priv->pending_local_ice_candidates->len == 1)
6316 ICE_UNLOCK (webrtc);
6319 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
6320 gst_webrtc_bin_enqueue_task (webrtc,
6321 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
6328 GstPromise *promise;
6332 _free_get_stats (struct get_stats *stats)
6335 gst_object_unref (stats->pad);
6337 gst_promise_unref (stats->promise);
6341 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
6342 static GstStructure *
6343 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
6345 /* Our selector is the pad,
6346 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
6349 return gst_webrtc_bin_create_stats (webrtc, stats->pad);
6353 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
6354 GstPromise * promise)
6356 struct get_stats *stats;
6358 g_return_if_fail (promise != NULL);
6359 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
6361 stats = g_new0 (struct get_stats, 1);
6362 stats->promise = gst_promise_ref (promise);
6363 /* FIXME: check that pad exists in element */
6365 stats->pad = gst_object_ref (pad);
6367 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
6368 stats, (GDestroyNotify) _free_get_stats, promise)) {
6370 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6371 "Could not retrieve statistics. webrtcbin is closed.");
6372 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6373 "error", G_TYPE_ERROR, error, NULL);
6375 gst_promise_reply (promise, s);
6377 g_clear_error (&error);
6381 static GstWebRTCRTPTransceiver *
6382 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
6383 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
6385 WebRTCTransceiver *trans;
6387 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
6393 _create_webrtc_transceiver (webrtc, direction, -1,
6394 webrtc_kind_from_caps (caps), caps);
6395 GST_LOG_OBJECT (webrtc,
6396 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
6400 return gst_object_ref (trans);
6404 _deref_and_unref (GstObject ** object)
6406 gst_clear_object (object);
6410 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
6412 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
6417 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
6419 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6420 GstWebRTCRTPTransceiver *trans =
6421 g_ptr_array_index (webrtc->priv->transceivers, i);
6422 gst_object_ref (trans);
6423 g_array_append_val (arr, trans);
6430 static GstWebRTCRTPTransceiver *
6431 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
6433 GstWebRTCRTPTransceiver *trans = NULL;
6437 if (idx >= webrtc->priv->transceivers->len) {
6438 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
6442 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
6443 gst_object_ref (trans);
6451 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
6455 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
6456 g_return_val_if_fail (uri != NULL, FALSE);
6458 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
6461 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
6468 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
6470 GstPad *gpad = GST_PAD_CAST (user_data);
6472 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
6473 gst_pad_store_sticky_event (gpad, *event);
6478 static WebRTCDataChannel *
6479 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
6480 GstStructure * init_params)
6483 gint max_packet_lifetime;
6484 gint max_retransmits;
6485 const gchar *protocol;
6486 gboolean negotiated;
6488 GstWebRTCPriorityType priority;
6489 WebRTCDataChannel *ret;
6490 gint max_channels = 65534;
6492 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
6493 g_return_val_if_fail (label != NULL, NULL);
6494 g_return_val_if_fail (strlen (label) <= 65535, NULL);
6495 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
6498 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
6501 || !gst_structure_get_int (init_params, "max-packet-lifetime",
6502 &max_packet_lifetime))
6503 max_packet_lifetime = -1;
6505 || !gst_structure_get_int (init_params, "max-retransmits",
6507 max_retransmits = -1;
6508 /* both retransmits and lifetime cannot be set */
6509 g_return_val_if_fail ((max_packet_lifetime == -1)
6510 || (max_retransmits == -1), NULL);
6513 || !(protocol = gst_structure_get_string (init_params, "protocol")))
6515 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
6518 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
6520 if (!negotiated || !init_params
6521 || !gst_structure_get_int (init_params, "id", &id))
6524 g_return_val_if_fail (id != -1, NULL);
6525 g_return_val_if_fail (id < 65535, NULL);
6528 || !gst_structure_get_enum (init_params, "priority",
6529 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
6530 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
6532 /* FIXME: clamp max-retransmits and max-packet-lifetime */
6534 if (webrtc->priv->sctp_transport) {
6535 /* Let transport be the connection's [[SctpTransport]] slot.
6537 * If the [[DataChannelId]] slot is not null, transport is in
6538 * connected state and [[DataChannelId]] is greater or equal to the
6539 * transport's [[MaxChannels]] slot, throw an OperationError.
6541 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
6544 g_return_val_if_fail (id <= max_channels, NULL);
6547 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
6548 !_have_sctp_elements (webrtc))
6553 /* check if the id has been used already */
6555 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
6557 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
6558 ("Attempting to add a data channel with a duplicate ID: %i", id),
6564 } else if (webrtc->current_local_description
6565 && webrtc->current_remote_description && webrtc->priv->sctp_transport
6566 && webrtc->priv->sctp_transport->transport) {
6567 /* else we can only generate an id if we're configured already. The other
6568 * case for generating an id is on sdp setting */
6569 id = _generate_data_channel_id (webrtc);
6571 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
6572 ("%s", "Failed to generate an identifier for a data channel"), NULL);
6579 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
6580 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
6581 "max-retransmits", max_retransmits, "protocol", protocol,
6582 "negotiated", negotiated, "id", id, "priority", priority, NULL);
6590 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
6591 gst_bin_add (GST_BIN (webrtc), ret->appsink);
6593 gst_element_sync_state_with_parent (ret->appsrc);
6594 gst_element_sync_state_with_parent (ret->appsink);
6596 ret = gst_object_ref (ret);
6597 ret->webrtcbin = webrtc;
6598 g_ptr_array_add (webrtc->priv->data_channels, ret);
6601 gst_webrtc_bin_update_sctp_priority (webrtc);
6602 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
6603 if (webrtc->priv->sctp_transport &&
6604 webrtc->priv->sctp_transport->association_established
6605 && !ret->parent.negotiated) {
6606 webrtc_data_channel_start_negotiation (ret);
6608 _update_need_negotiation (webrtc);
6615 /* === rtpbin signal implementations === */
6618 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
6619 GstWebRTCBin * webrtc)
6621 gchar *new_pad_name = NULL;
6623 new_pad_name = gst_pad_get_name (new_pad);
6624 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
6625 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
6626 guint32 session_id = 0, ssrc = 0, pt = 0;
6627 GstWebRTCRTPTransceiver *rtp_trans;
6628 WebRTCTransceiver *trans;
6629 TransportStream *stream;
6630 GstWebRTCBinPad *pad;
6631 guint media_idx = 0;
6632 gboolean found_ssrc = FALSE;
6635 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
6637 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
6641 stream = _find_transport_for_session (webrtc, session_id);
6643 g_warn_if_reached ();
6645 media_idx = session_id;
6647 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
6648 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
6649 if (item->ssrc == ssrc) {
6650 media_idx = item->media_idx;
6657 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
6660 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
6662 g_warn_if_reached ();
6663 trans = WEBRTC_TRANSCEIVER (rtp_trans);
6664 g_assert (trans->stream == stream);
6666 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
6668 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
6669 " for rtpbin pad name %s", pad, new_pad_name);
6670 if (!_remove_pending_pad (webrtc, pad)) {
6671 /* assumption here is that rtpbin doesn't duplicate pads and that if
6672 * there is no pending pad, this is a duplicate stream for e.g. simulcast
6674 gst_clear_object (&pad);
6676 _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT);
6677 GST_TRACE_OBJECT (webrtc,
6678 "duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %"
6679 GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name);
6680 gst_object_ref_sink (pad);
6684 g_warn_if_reached ();
6685 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
6687 if (webrtc->priv->running)
6688 gst_pad_set_active (GST_PAD (pad), TRUE);
6692 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
6693 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
6695 gst_object_unref (pad);
6697 g_free (new_pad_name);
6700 /* only used for the receiving streams */
6702 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
6703 GstWebRTCBin * webrtc)
6705 TransportStream *stream;
6708 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
6711 stream = _find_transport_for_session (webrtc, session_id);
6713 goto unknown_session;
6715 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
6718 GST_DEBUG_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
6719 "session %d", ret, pt, session_id);
6725 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
6731 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
6732 GstWebRTCBin * webrtc)
6734 TransportStream *stream;
6735 GstElement *ret, *rtx;
6739 stream = _find_transport_for_session (webrtc, session_id);
6741 /* a rtp session without a stream is a webrtcbin bug */
6742 g_warn_if_reached ();
6746 if (stream->rtxsend) {
6747 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
6748 g_warn_if_reached ();
6752 GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
6753 "stream %" GST_PTR_FORMAT, session_id, stream);
6755 ret = gst_bin_new (NULL);
6756 rtx = gst_element_factory_make ("rtprtxsend", NULL);
6757 /* XXX: allow control from outside? */
6758 g_object_set (rtx, "max-size-packets", 500, NULL);
6760 if (!gst_bin_add (GST_BIN (ret), rtx))
6761 g_warn_if_reached ();
6763 stream->rtxsend = gst_object_ref (rtx);
6764 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
6766 name = g_strdup_printf ("src_%u", session_id);
6767 pad = gst_element_get_static_pad (rtx, "src");
6768 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
6769 g_warn_if_reached ();
6770 gst_clear_object (&pad);
6771 g_clear_pointer (&name, g_free);
6773 name = g_strdup_printf ("sink_%u", session_id);
6774 pad = gst_element_get_static_pad (rtx, "sink");
6775 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
6776 g_warn_if_reached ();
6777 gst_clear_object (&pad);
6778 g_clear_pointer (&name, g_free);
6784 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
6785 GstWebRTCBin * webrtc)
6787 TransportStream *stream;
6788 GstPad *pad, *ghost;
6792 stream = _find_transport_for_session (webrtc, session_id);
6794 /* no transport stream before the session has been created is a webrtcbin
6795 * programming error! */
6796 g_warn_if_reached ();
6800 if (stream->rtxreceive) {
6801 GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
6802 g_warn_if_reached ();
6806 if (stream->reddec) {
6807 GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
6808 g_warn_if_reached ();
6812 GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
6813 "stream %" GST_PTR_FORMAT, session_id, stream);
6815 ret = gst_bin_new (NULL);
6817 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
6818 gst_object_ref (stream->rtxreceive);
6819 if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
6820 g_warn_if_reached ();
6822 stream->reddec = gst_element_factory_make ("rtpreddec", NULL);
6823 gst_object_ref (stream->reddec);
6824 if (!gst_bin_add (GST_BIN (ret), stream->reddec))
6825 g_warn_if_reached ();
6827 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
6829 if (!gst_element_link (stream->rtxreceive, stream->reddec))
6830 g_warn_if_reached ();
6832 name = g_strdup_printf ("sink_%u", session_id);
6833 pad = gst_element_get_static_pad (stream->rtxreceive, "sink");
6834 ghost = gst_ghost_pad_new (name, pad);
6835 g_clear_pointer (&name, g_free);
6836 gst_clear_object (&pad);
6837 if (!gst_element_add_pad (ret, ghost))
6838 g_warn_if_reached ();
6840 name = g_strdup_printf ("src_%u", session_id);
6841 pad = gst_element_get_static_pad (stream->reddec, "src");
6842 ghost = gst_ghost_pad_new (name, pad);
6843 g_clear_pointer (&name, g_free);
6844 gst_clear_object (&pad);
6845 if (!gst_element_add_pad (ret, ghost))
6846 g_warn_if_reached ();
6852 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
6853 guint ssrc, guint pt, GstWebRTCBin * webrtc)
6855 TransportStream *stream;
6856 GstElement *ret = NULL;
6857 GObject *internal_storage;
6859 stream = _find_transport_for_session (webrtc, session_id);
6861 /* a rtp session without a stream is a webrtcbin bug */
6862 g_warn_if_reached ();
6866 /* TODO: for now, we only support ulpfec, but once we support
6867 * more algorithms, if the remote may use more than one algorithm,
6868 * we will want to do the following:
6870 * + Return a bin here, with the relevant FEC decoders plugged in
6871 * and their payload type set to 0
6873 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
6874 "stream %" GST_PTR_FORMAT, pt, session_id, stream);
6876 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
6878 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
6881 g_object_set (ret, "storage", internal_storage, NULL);
6882 g_clear_object (&internal_storage);
6884 g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
6885 GINT_TO_POINTER (pt));
6888 stream->fecdecs = g_list_prepend (stream->fecdecs, gst_object_ref (ret));
6889 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
6896 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6897 GstWebRTCBin * webrtc)
6899 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
6903 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6904 GstWebRTCBin * webrtc)
6906 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
6910 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6911 GstWebRTCBin * webrtc)
6913 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
6918 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6919 GstWebRTCBin * webrtc)
6921 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
6925 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6926 GstWebRTCBin * webrtc)
6928 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
6932 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
6933 GstWebRTCBin * webrtc)
6935 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
6939 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
6940 GstWebRTCBin * webrtc)
6942 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
6946 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
6947 GstWebRTCBin * webrtc)
6949 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
6953 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6954 GstWebRTCBin * webrtc)
6956 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
6960 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6961 GstWebRTCBin * webrtc)
6963 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
6968 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6969 GstWebRTCBin * webrtc)
6971 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
6976 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
6977 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
6979 TransportStream *stream;
6983 GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
6984 "session %u ssrc %u", jitterbuffer, session_id, ssrc);
6986 if (!(stream = _find_transport_for_session (webrtc, session_id))) {
6987 g_warn_if_reached ();
6991 /* XXX: this will fail with no ssrc in the remote sdp as used with e.g. simulcast
6992 * newer SDP versions from chrome/firefox */
6993 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
6994 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
6996 if (item->ssrc == ssrc) {
6997 GstWebRTCRTPTransceiver *trans;
7000 trans = _find_transceiver_for_mline (webrtc, item->media_idx);
7002 g_warn_if_reached ();
7006 do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
7007 /* We don't set do-retransmission on rtpbin as we want per-session control */
7008 GST_LOG_OBJECT (webrtc, "setting do-nack=%s for transceiver %"
7009 GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
7010 " rtp session %u ssrc %u", do_nack ? "true" : "false", trans, stream,
7012 g_object_set (jitterbuffer, "do-retransmission", do_nack, NULL);
7014 g_weak_ref_set (&item->rtpjitterbuffer, jitterbuffer);
7023 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
7024 guint session_id, GstWebRTCBin * webrtc)
7026 guint64 latency = webrtc->priv->jb_latency;
7028 /* Add an extra 50 ms for safey */
7029 latency += RTPSTORAGE_EXTRA_TIME;
7030 latency *= GST_MSECOND;
7032 g_object_set (storage, "size-time", latency, NULL);
7036 _create_rtpbin (GstWebRTCBin * webrtc)
7040 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
7043 /* mandated by WebRTC */
7044 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
7046 g_object_set (rtpbin, "do-lost", TRUE, NULL);
7048 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
7050 g_signal_connect (rtpbin, "request-pt-map",
7051 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
7052 g_signal_connect (rtpbin, "request-aux-sender",
7053 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
7054 g_signal_connect (rtpbin, "request-aux-receiver",
7055 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
7056 g_signal_connect (rtpbin, "new-storage",
7057 G_CALLBACK (on_rtpbin_new_storage), webrtc);
7058 g_signal_connect (rtpbin, "request-fec-decoder-full",
7059 G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
7060 g_signal_connect (rtpbin, "on-bye-ssrc",
7061 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
7062 g_signal_connect (rtpbin, "on-bye-timeout",
7063 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
7064 g_signal_connect (rtpbin, "on-new-ssrc",
7065 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
7066 g_signal_connect (rtpbin, "on-new-sender-ssrc",
7067 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
7068 g_signal_connect (rtpbin, "on-sender-ssrc-active",
7069 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
7070 g_signal_connect (rtpbin, "on-sender-timeout",
7071 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
7072 g_signal_connect (rtpbin, "on-ssrc-active",
7073 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
7074 g_signal_connect (rtpbin, "on-ssrc-collision",
7075 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
7076 g_signal_connect (rtpbin, "on-ssrc-sdes",
7077 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
7078 g_signal_connect (rtpbin, "on-ssrc-validated",
7079 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
7080 g_signal_connect (rtpbin, "on-timeout",
7081 G_CALLBACK (on_rtpbin_timeout), webrtc);
7082 g_signal_connect (rtpbin, "new-jitterbuffer",
7083 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
7088 static GstStateChangeReturn
7089 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
7091 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7092 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
7094 GST_DEBUG ("changing state: %s => %s",
7095 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
7096 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
7098 switch (transition) {
7099 case GST_STATE_CHANGE_NULL_TO_READY:{
7100 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
7101 return GST_STATE_CHANGE_FAILURE;
7102 _start_thread (webrtc);
7104 _update_need_negotiation (webrtc);
7108 case GST_STATE_CHANGE_READY_TO_PAUSED:
7109 webrtc->priv->running = TRUE;
7115 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
7116 if (ret == GST_STATE_CHANGE_FAILURE)
7119 switch (transition) {
7120 case GST_STATE_CHANGE_READY_TO_PAUSED:
7121 /* Mangle the return value to NO_PREROLL as that's what really is
7122 * occurring here however cannot be propagated correctly due to nicesrc
7123 * requiring that it be in PLAYING already in order to send/receive
7125 ret = GST_STATE_CHANGE_NO_PREROLL;
7127 case GST_STATE_CHANGE_PAUSED_TO_READY:
7128 webrtc->priv->running = FALSE;
7130 case GST_STATE_CHANGE_READY_TO_NULL:
7131 _stop_thread (webrtc);
7140 static GstPadProbeReturn
7141 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
7143 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
7145 return GST_PAD_PROBE_OK;
7150 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
7151 const gchar * name, const GstCaps * caps)
7153 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7154 GstWebRTCRTPTransceiver *trans = NULL;
7155 GstWebRTCBinPad *pad = NULL;
7157 gboolean lock_mline = FALSE;
7159 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
7162 if (templ->direction != GST_PAD_SINK ||
7163 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
7164 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
7170 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
7171 /* no name given when requesting the pad, use next available int */
7172 serial = webrtc->priv->max_sink_pad_serial++;
7174 /* parse serial number from requested padname */
7175 serial = g_ascii_strtoull (&name[5], NULL, 10);
7180 GstWebRTCBinPad *pad2;
7182 trans = _find_transceiver_for_mline (webrtc, serial);
7185 /* Reject transceivers that are only for receiving ... */
7186 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
7187 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
7189 g_enum_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
7191 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7192 " existing m-line %d, but the transceiver's direction is %s",
7193 name, serial, direction);
7198 /* Reject transceivers that already have a pad allocated */
7199 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
7201 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
7202 " but the transceiver associated with this m-line already has pad"
7203 " %s", name, serial, GST_PAD_NAME (pad2));
7204 gst_object_unref (pad2);
7209 GST_OBJECT_LOCK (trans);
7210 if (trans->codec_preferences &&
7211 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
7212 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7213 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
7214 " don't match existing codec preferences %" GST_PTR_FORMAT,
7215 name, serial, caps, trans->codec_preferences);
7216 GST_OBJECT_UNLOCK (trans);
7219 GST_OBJECT_UNLOCK (trans);
7221 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
7222 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
7224 if (trans->kind != kind) {
7225 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7226 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
7227 " don't match transceiver kind %d",
7228 name, serial, caps, trans->kind);
7236 /* Let's try to find a free transceiver that matches */
7238 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
7241 kind = webrtc_kind_from_caps (caps);
7243 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
7244 GstWebRTCRTPTransceiver *tmptrans =
7245 g_ptr_array_index (webrtc->priv->transceivers, i);
7246 GstWebRTCBinPad *pad2;
7247 gboolean has_matching_caps;
7249 /* Ignore transceivers with a non-matching kind */
7250 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
7251 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
7254 /* Ignore stopped transmitters */
7255 if (tmptrans->stopped)
7258 /* Ignore transceivers that are only for receiving ... */
7259 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
7260 || tmptrans->direction ==
7261 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
7264 /* Ignore transceivers that already have a pad allocated */
7265 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
7267 gst_object_unref (pad2);
7271 GST_OBJECT_LOCK (tmptrans);
7272 has_matching_caps = (caps && tmptrans->codec_preferences &&
7273 !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
7274 GST_OBJECT_UNLOCK (tmptrans);
7275 /* Ignore transceivers with non-matching caps */
7276 if (!has_matching_caps)
7285 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
7286 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
7287 webrtc_kind_from_caps (caps), NULL));
7288 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
7290 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
7291 " for mline %u", trans, serial);
7293 if (!_update_transceiver_kind_from_caps (trans, caps)) {
7294 GstWebRTCKind caps_kind = webrtc_kind_from_caps (caps);
7296 GST_WARNING_OBJECT (webrtc,
7297 "Trying to change kind of transceiver %" GST_PTR_FORMAT
7298 " at m-line %d from %s (%d) to %s (%d)", trans, serial,
7299 gst_webrtc_kind_to_string (trans->kind), trans->kind,
7300 gst_webrtc_kind_to_string (caps_kind), caps_kind);
7304 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
7306 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
7307 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
7308 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
7309 webrtc->priv->pending_sink_transceivers =
7310 g_list_append (webrtc->priv->pending_sink_transceivers,
7311 gst_object_ref (pad));
7314 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
7315 wtrans->mline_locked = TRUE;
7316 trans->mline = serial;
7321 _add_pad (webrtc, pad);
7323 return GST_PAD (pad);
7331 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
7333 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7334 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7336 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
7338 /* remove the transceiver from the pad so that subsequent code doesn't use
7339 * a possibly dead transceiver */
7341 if (webrtc_pad->trans)
7342 gst_object_unref (webrtc_pad->trans);
7343 webrtc_pad->trans = NULL;
7344 gst_caps_replace (&webrtc_pad->received_caps, NULL);
7347 _remove_pad (webrtc, webrtc_pad);
7350 _update_need_negotiation (webrtc);
7355 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
7360 /* Add an extra 50 ms for safety */
7361 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
7362 latency_ns *= GST_MSECOND;
7364 for (i = 0; i < webrtc->priv->transports->len; i++) {
7365 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
7366 GObject *storage = NULL;
7368 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
7371 g_object_set (storage, "size-time", latency_ns, NULL);
7373 g_object_unref (storage);
7378 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
7379 const GValue * value, GParamSpec * pspec)
7381 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7384 case PROP_STUN_SERVER:
7385 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
7386 g_value_get_string (value));
7388 case PROP_TURN_SERVER:
7389 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
7390 g_value_get_string (value));
7392 case PROP_BUNDLE_POLICY:
7393 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
7394 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
7396 webrtc->bundle_policy = g_value_get_enum (value);
7399 case PROP_ICE_TRANSPORT_POLICY:
7400 webrtc->ice_transport_policy = g_value_get_enum (value);
7401 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
7402 webrtc->ice_transport_policy ==
7403 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
7406 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
7407 webrtc->priv->jb_latency = g_value_get_uint (value);
7408 _update_rtpstorage_latency (webrtc);
7411 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7417 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
7418 GValue * value, GParamSpec * pspec)
7420 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7424 case PROP_CONNECTION_STATE:
7425 g_value_set_enum (value, webrtc->peer_connection_state);
7427 case PROP_SIGNALING_STATE:
7428 g_value_set_enum (value, webrtc->signaling_state);
7430 case PROP_ICE_GATHERING_STATE:
7431 g_value_set_enum (value, webrtc->ice_gathering_state);
7433 case PROP_ICE_CONNECTION_STATE:
7434 g_value_set_enum (value, webrtc->ice_connection_state);
7436 case PROP_LOCAL_DESCRIPTION:
7437 if (webrtc->pending_local_description)
7438 g_value_set_boxed (value, webrtc->pending_local_description);
7439 else if (webrtc->current_local_description)
7440 g_value_set_boxed (value, webrtc->current_local_description);
7442 g_value_set_boxed (value, NULL);
7444 case PROP_CURRENT_LOCAL_DESCRIPTION:
7445 g_value_set_boxed (value, webrtc->current_local_description);
7447 case PROP_PENDING_LOCAL_DESCRIPTION:
7448 g_value_set_boxed (value, webrtc->pending_local_description);
7450 case PROP_REMOTE_DESCRIPTION:
7451 if (webrtc->pending_remote_description)
7452 g_value_set_boxed (value, webrtc->pending_remote_description);
7453 else if (webrtc->current_remote_description)
7454 g_value_set_boxed (value, webrtc->current_remote_description);
7456 g_value_set_boxed (value, NULL);
7458 case PROP_CURRENT_REMOTE_DESCRIPTION:
7459 g_value_set_boxed (value, webrtc->current_remote_description);
7461 case PROP_PENDING_REMOTE_DESCRIPTION:
7462 g_value_set_boxed (value, webrtc->pending_remote_description);
7464 case PROP_STUN_SERVER:
7465 g_value_take_string (value,
7466 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
7468 case PROP_TURN_SERVER:
7469 g_value_take_string (value,
7470 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
7472 case PROP_BUNDLE_POLICY:
7473 g_value_set_enum (value, webrtc->bundle_policy);
7475 case PROP_ICE_TRANSPORT_POLICY:
7476 g_value_set_enum (value, webrtc->ice_transport_policy);
7478 case PROP_ICE_AGENT:
7479 g_value_set_object (value, webrtc->priv->ice);
7482 g_value_set_uint (value, webrtc->priv->jb_latency);
7484 case PROP_SCTP_TRANSPORT:
7485 g_value_set_object (value, webrtc->priv->sctp_transport);
7488 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7495 gst_webrtc_bin_constructed (GObject * object)
7497 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7500 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
7501 webrtc->priv->ice = gst_webrtc_ice_new (name);
7503 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
7504 (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
7508 G_OBJECT_CLASS (parent_class)->constructed (object);
7512 _free_pending_pad (GstPad * pad)
7514 gst_object_unref (pad);
7518 gst_webrtc_bin_dispose (GObject * object)
7520 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7522 if (webrtc->priv->ice)
7523 gst_object_unref (webrtc->priv->ice);
7524 webrtc->priv->ice = NULL;
7526 if (webrtc->priv->ice_stream_map)
7527 g_array_free (webrtc->priv->ice_stream_map, TRUE);
7528 webrtc->priv->ice_stream_map = NULL;
7530 g_clear_object (&webrtc->priv->sctp_transport);
7532 G_OBJECT_CLASS (parent_class)->dispose (object);
7536 gst_webrtc_bin_finalize (GObject * object)
7538 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7540 if (webrtc->priv->transports)
7541 g_ptr_array_free (webrtc->priv->transports, TRUE);
7542 webrtc->priv->transports = NULL;
7544 if (webrtc->priv->transceivers)
7545 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
7546 webrtc->priv->transceivers = NULL;
7548 if (webrtc->priv->data_channels)
7549 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
7550 webrtc->priv->data_channels = NULL;
7552 if (webrtc->priv->pending_data_channels)
7553 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
7554 webrtc->priv->pending_data_channels = NULL;
7556 if (webrtc->priv->pending_remote_ice_candidates)
7557 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
7558 webrtc->priv->pending_remote_ice_candidates = NULL;
7560 if (webrtc->priv->pending_local_ice_candidates)
7561 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
7562 webrtc->priv->pending_local_ice_candidates = NULL;
7564 if (webrtc->priv->pending_pads)
7565 g_list_free_full (webrtc->priv->pending_pads,
7566 (GDestroyNotify) _free_pending_pad);
7567 webrtc->priv->pending_pads = NULL;
7569 if (webrtc->priv->pending_sink_transceivers)
7570 g_list_free_full (webrtc->priv->pending_sink_transceivers,
7571 (GDestroyNotify) gst_object_unref);
7572 webrtc->priv->pending_sink_transceivers = NULL;
7574 if (webrtc->current_local_description)
7575 gst_webrtc_session_description_free (webrtc->current_local_description);
7576 webrtc->current_local_description = NULL;
7577 if (webrtc->pending_local_description)
7578 gst_webrtc_session_description_free (webrtc->pending_local_description);
7579 webrtc->pending_local_description = NULL;
7581 if (webrtc->current_remote_description)
7582 gst_webrtc_session_description_free (webrtc->current_remote_description);
7583 webrtc->current_remote_description = NULL;
7584 if (webrtc->pending_remote_description)
7585 gst_webrtc_session_description_free (webrtc->pending_remote_description);
7586 webrtc->pending_remote_description = NULL;
7588 if (webrtc->priv->last_generated_answer)
7589 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
7590 webrtc->priv->last_generated_answer = NULL;
7591 if (webrtc->priv->last_generated_offer)
7592 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
7593 webrtc->priv->last_generated_offer = NULL;
7595 g_mutex_clear (DC_GET_LOCK (webrtc));
7596 g_mutex_clear (ICE_GET_LOCK (webrtc));
7597 g_mutex_clear (PC_GET_LOCK (webrtc));
7598 g_cond_clear (PC_GET_COND (webrtc));
7600 G_OBJECT_CLASS (parent_class)->finalize (object);
7604 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
7606 GObjectClass *gobject_class = (GObjectClass *) klass;
7607 GstElementClass *element_class = (GstElementClass *) klass;
7609 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
7610 element_class->release_pad = gst_webrtc_bin_release_pad;
7611 element_class->change_state = gst_webrtc_bin_change_state;
7613 gst_element_class_add_static_pad_template_with_gtype (element_class,
7614 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
7615 gst_element_class_add_static_pad_template (element_class, &src_template);
7617 gst_element_class_set_metadata (element_class, "WebRTC Bin",
7618 "Filter/Network/WebRTC", "A bin for webrtc connections",
7619 "Matthew Waters <matthew@centricular.com>");
7621 gobject_class->constructed = gst_webrtc_bin_constructed;
7622 gobject_class->get_property = gst_webrtc_bin_get_property;
7623 gobject_class->set_property = gst_webrtc_bin_set_property;
7624 gobject_class->dispose = gst_webrtc_bin_dispose;
7625 gobject_class->finalize = gst_webrtc_bin_finalize;
7627 g_object_class_install_property (gobject_class,
7628 PROP_LOCAL_DESCRIPTION,
7629 g_param_spec_boxed ("local-description", "Local Description",
7630 "The local SDP description in use for this connection. "
7631 "Favours a pending description over the current description",
7632 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7633 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7635 g_object_class_install_property (gobject_class,
7636 PROP_CURRENT_LOCAL_DESCRIPTION,
7637 g_param_spec_boxed ("current-local-description",
7638 "Current Local Description",
7639 "The local description that was successfully negotiated the last time "
7640 "the connection transitioned into the stable state",
7641 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7642 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7644 g_object_class_install_property (gobject_class,
7645 PROP_PENDING_LOCAL_DESCRIPTION,
7646 g_param_spec_boxed ("pending-local-description",
7647 "Pending Local Description",
7648 "The local description that is in the process of being negotiated plus "
7649 "any local candidates that have been generated by the ICE Agent since the "
7650 "offer or answer was created",
7651 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7652 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7654 g_object_class_install_property (gobject_class,
7655 PROP_REMOTE_DESCRIPTION,
7656 g_param_spec_boxed ("remote-description", "Remote Description",
7657 "The remote SDP description to use for this connection. "
7658 "Favours a pending description over the current description",
7659 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7660 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7662 g_object_class_install_property (gobject_class,
7663 PROP_CURRENT_REMOTE_DESCRIPTION,
7664 g_param_spec_boxed ("current-remote-description",
7665 "Current Remote Description",
7666 "The last remote description that was successfully negotiated the last "
7667 "time the connection transitioned into the stable state plus any remote "
7668 "candidates that have been supplied via addIceCandidate() since the offer "
7669 "or answer was created",
7670 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7671 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7673 g_object_class_install_property (gobject_class,
7674 PROP_PENDING_REMOTE_DESCRIPTION,
7675 g_param_spec_boxed ("pending-remote-description",
7676 "Pending Remote Description",
7677 "The remote description that is in the process of being negotiated, "
7678 "complete with any remote candidates that have been supplied via "
7679 "addIceCandidate() since the offer or answer was created",
7680 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7681 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7683 g_object_class_install_property (gobject_class,
7685 g_param_spec_string ("stun-server", "STUN Server",
7686 "The STUN server of the form stun://hostname:port",
7687 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7689 g_object_class_install_property (gobject_class,
7691 g_param_spec_string ("turn-server", "TURN Server",
7692 "The TURN server of the form turn(s)://username:password@host:port. "
7693 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
7694 "if you wish to use multiple TURN servers",
7695 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7697 g_object_class_install_property (gobject_class,
7698 PROP_CONNECTION_STATE,
7699 g_param_spec_enum ("connection-state", "Connection State",
7700 "The overall connection state of this element",
7701 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
7702 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
7703 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7705 g_object_class_install_property (gobject_class,
7706 PROP_SIGNALING_STATE,
7707 g_param_spec_enum ("signaling-state", "Signaling State",
7708 "The signaling state of this element",
7709 GST_TYPE_WEBRTC_SIGNALING_STATE,
7710 GST_WEBRTC_SIGNALING_STATE_STABLE,
7711 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7713 g_object_class_install_property (gobject_class,
7714 PROP_ICE_CONNECTION_STATE,
7715 g_param_spec_enum ("ice-connection-state", "ICE connection state",
7716 "The collective connection state of all ICETransport's",
7717 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
7718 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
7719 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7721 g_object_class_install_property (gobject_class,
7722 PROP_ICE_GATHERING_STATE,
7723 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
7724 "The collective gathering state of all ICETransport's",
7725 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
7726 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
7727 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7729 g_object_class_install_property (gobject_class,
7731 g_param_spec_enum ("bundle-policy", "Bundle Policy",
7732 "The policy to apply for bundling",
7733 GST_TYPE_WEBRTC_BUNDLE_POLICY,
7734 GST_WEBRTC_BUNDLE_POLICY_NONE,
7735 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7737 g_object_class_install_property (gobject_class,
7738 PROP_ICE_TRANSPORT_POLICY,
7739 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
7740 "The policy to apply for ICE transport",
7741 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
7742 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
7743 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7745 g_object_class_install_property (gobject_class,
7747 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
7748 "The WebRTC ICE agent",
7749 GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7752 * GstWebRTCBin:latency:
7754 * Default duration to buffer in the jitterbuffers (in ms)
7759 g_object_class_install_property (gobject_class,
7761 g_param_spec_uint ("latency", "Latency",
7762 "Default duration to buffer in the jitterbuffers (in ms)",
7763 0, G_MAXUINT, DEFAULT_JB_LATENCY,
7764 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7767 * GstWebRTCBin:sctp-transport:
7769 * The WebRTC SCTP Transport
7773 g_object_class_install_property (gobject_class,
7774 PROP_SCTP_TRANSPORT,
7775 g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
7776 "The WebRTC SCTP Transport",
7777 GST_TYPE_WEBRTC_SCTP_TRANSPORT,
7778 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7781 * GstWebRTCBin::create-offer:
7782 * @object: the #webrtcbin
7783 * @options: (nullable): create-offer options
7784 * @promise: a #GstPromise which will contain the offer
7786 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
7787 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
7788 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7789 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
7790 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7793 * GstWebRTCBin::create-answer:
7794 * @object: the #webrtcbin
7795 * @options: (nullable): create-answer options
7796 * @promise: a #GstPromise which will contain the answer
7798 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
7799 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
7800 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7801 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
7802 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7805 * GstWebRTCBin::set-local-description:
7806 * @object: the #GstWebRTCBin
7807 * @desc: a #GstWebRTCSessionDescription description
7808 * @promise: (nullable): a #GstPromise to be notified when it's set
7810 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
7811 g_signal_new_class_handler ("set-local-description",
7812 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7813 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
7814 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7817 * GstWebRTCBin::set-remote-description:
7818 * @object: the #GstWebRTCBin
7819 * @desc: a #GstWebRTCSessionDescription description
7820 * @promise: (nullable): a #GstPromise to be notified when it's set
7822 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
7823 g_signal_new_class_handler ("set-remote-description",
7824 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7825 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
7826 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7829 * GstWebRTCBin::add-ice-candidate:
7830 * @object: the #webrtcbin
7831 * @mline_index: the index of the media description in the SDP
7832 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
7835 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
7836 g_signal_new_class_handler ("add-ice-candidate",
7837 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7838 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
7839 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7842 * GstWebRTCBin::get-stats:
7843 * @object: the #webrtcbin
7844 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
7845 * @promise: a #GstPromise for the result
7847 * The @promise will contain the result of retrieving the session statistics.
7848 * The structure will be named 'application/x-webrtc-stats and contain the
7849 * following based on the webrtc-stats spec available from
7850 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
7851 * and is constantly changing these statistics may be changed to fit with
7854 * Each field key is a unique identifier for each RTCStats
7855 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
7856 * GstStructure) in the RTCStatsReport
7857 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
7858 * field in the RTCStats subclass is outlined below.
7860 * Each statistics structure contains the following values as defined by
7861 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
7863 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
7864 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
7865 * "id" G_TYPE_STRING unique identifier
7867 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
7869 * "payload-type" G_TYPE_UINT the rtp payload number in use
7870 * "clock-rate" G_TYPE_UINT the rtp clock-rate
7872 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
7874 * "ssrc" G_TYPE_STRING the rtp sequence src in use
7875 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
7876 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
7878 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
7880 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
7881 * "packets-lost" G_TYPE_UINT64 number of packets lost
7882 * "packets-discarded" G_TYPE_UINT64 number of packets discarded
7883 * "packets-repaired" G_TYPE_UINT64 number of packets repaired
7884 * "jitter" G_TYPE_DOUBLE packet jitter measured in seconds
7886 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
7888 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
7889 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
7890 * "packets-duplicated" G_TYPE_UINT64 number of packets duplicated
7891 * "fir-count" G_TYPE_UINT FIR packets sent by the receiver
7892 * "pli-count" G_TYPE_UINT PLI packets sent by the receiver
7893 * "nack-count" G_TYPE_UINT NACK packets sent by the receiver
7895 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
7897 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
7898 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
7899 * "fraction-lost" G_TYPE_DOUBLE fraction packet loss
7901 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
7903 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7904 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7906 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
7908 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
7909 * "fir-count" G_TYPE_UINT FIR packets received by the sender
7910 * "pli-count" G_TYPE_UINT PLI packets received by the sender
7911 * "nack-count" G_TYPE_UINT NACK packets received by the sender
7913 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
7915 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
7916 * "remote-timestamp" G_TYPE_DOUBLE remote timestamp the statistics were sent by the remote
7919 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
7920 g_signal_new_class_handler ("get-stats",
7921 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7922 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
7923 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
7926 * GstWebRTCBin::on-negotiation-needed:
7927 * @object: the #webrtcbin
7929 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
7930 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
7931 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
7934 * GstWebRTCBin::on-ice-candidate:
7935 * @object: the #webrtcbin
7936 * @mline_index: the index of the media description in the SDP
7937 * @candidate: the ICE candidate
7939 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
7940 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
7941 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7942 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7945 * GstWebRTCBin::on-new-transceiver:
7946 * @object: the #webrtcbin
7947 * @candidate: the new #GstWebRTCRTPTransceiver
7949 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
7950 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
7951 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7952 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
7955 * GstWebRTCBin::on-data-channel:
7956 * @object: the #GstWebRTCBin
7957 * @candidate: the new `GstWebRTCDataChannel`
7959 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
7960 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
7961 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7962 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
7965 * GstWebRTCBin::add-transceiver:
7966 * @object: the #webrtcbin
7967 * @direction: the direction of the new transceiver
7968 * @caps: (allow none): the codec preferences for this transceiver
7970 * Returns: the new #GstWebRTCRTPTransceiver
7972 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
7973 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
7974 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7975 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
7976 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
7977 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
7980 * GstWebRTCBin::get-transceivers:
7981 * @object: the #webrtcbin
7983 * Returns: a #GArray of #GstWebRTCRTPTransceivers
7985 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
7986 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
7987 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7988 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
7992 * GstWebRTCBin::get-transceiver:
7993 * @object: the #GstWebRTCBin
7994 * @idx: The index of the transceiver
7996 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
7999 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
8000 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
8001 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8002 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
8003 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
8006 * GstWebRTCBin::add-turn-server:
8007 * @object: the #GstWebRTCBin
8008 * @uri: The uri of the server of the form turn(s)://username:password@host:port
8010 * Add a turn server to obtain ICE candidates from
8012 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
8013 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
8014 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8015 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
8016 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
8019 * GstWebRTCBin::create-data-channel:
8020 * @object: the #GstWebRTCBin
8021 * @label: the label for the data channel
8022 * @options: a #GstStructure of options for creating the data channel
8024 * The options dictionary is the same format as the RTCDataChannelInit
8025 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
8026 * and reproduced below
8028 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
8029 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
8030 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
8031 * protocol G_TYPE_STRING The subprotocol used by this channel
8032 * 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.
8033 * id G_TYPE_INT Override the default identifier selection of this channel
8034 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
8036 * Returns: (transfer full): a new data channel object
8038 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
8039 g_signal_new_class_handler ("create-data-channel",
8040 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8041 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
8042 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
8044 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
8045 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
8049 _unparent_and_unref (GObject * object)
8051 GstObject *obj = GST_OBJECT (object);
8053 GST_OBJECT_PARENT (obj) = NULL;
8055 gst_object_unref (obj);
8059 _transport_free (GObject * object)
8061 TransportStream *stream = (TransportStream *) object;
8062 GstWebRTCBin *webrtc;
8064 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
8066 if (stream->transport) {
8067 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
8068 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
8071 gst_object_unref (object);
8075 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
8077 /* Set SINK/SRC flags as webrtcbin can act as one depending on the
8078 * SDP later. Without setting this here already, surrounding bins might not
8079 * notice this and the pipeline configuration might become inconsistent,
8080 * e.g. with regards to latency.
8081 * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
8083 gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
8084 GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
8085 GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
8087 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
8088 g_mutex_init (PC_GET_LOCK (webrtc));
8089 g_cond_init (PC_GET_COND (webrtc));
8091 g_mutex_init (ICE_GET_LOCK (webrtc));
8092 g_mutex_init (DC_GET_LOCK (webrtc));
8094 webrtc->rtpbin = _create_rtpbin (webrtc);
8095 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
8097 webrtc->priv->transceivers =
8098 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
8099 webrtc->priv->transports =
8100 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
8102 webrtc->priv->data_channels =
8103 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
8105 webrtc->priv->pending_data_channels =
8106 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
8108 webrtc->priv->ice_stream_map =
8109 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
8110 webrtc->priv->pending_remote_ice_candidates =
8111 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
8112 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
8113 (GDestroyNotify) _clear_ice_candidate_item);
8115 webrtc->priv->pending_local_ice_candidates =
8116 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
8117 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
8118 (GDestroyNotify) _clear_ice_candidate_item);
8120 /* we start off closed until we move to READY */
8121 webrtc->priv->is_closed = TRUE;
8122 webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;