2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31 #include "webrtcdatachannel.h"
32 #include "sctptransport.h"
34 #include <gst/rtp/rtp.h>
40 #define RANDOM_SESSION_ID \
41 ((((((guint64) g_random_int()) << 32) | \
42 (guint64) g_random_int ())) & \
43 G_GUINT64_CONSTANT (0x7fffffffffffffff))
45 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
46 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
47 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
49 #define PC_GET_COND(w) (&w->priv->pc_cond)
50 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
51 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
52 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
54 #define ICE_GET_LOCK(w) (&w->priv->ice_lock)
55 #define ICE_LOCK(w) (g_mutex_lock (ICE_GET_LOCK(w)))
56 #define ICE_UNLOCK(w) (g_mutex_unlock (ICE_GET_LOCK(w)))
59 /* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */
60 #define RTPSTORAGE_EXTRA_TIME (50)
63 * This webrtcbin implements the majority of the W3's peerconnection API and
64 * implementation guide where possible. Generating offers, answers and setting
65 * local and remote SDP's are all supported. Both media descriptions and
66 * descriptions involving data channels are supported.
68 * Each input/output pad is equivalent to a Track in W3 parlance which are
69 * added/removed from the bin. The number of requested sink pads is the number
70 * of streams that will be sent to the receiver and will be associated with a
71 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
73 * On the receiving side, RTPTransceiver's are created in response to setting
74 * a remote description. Output pads for the receiving streams in the set
75 * description are also created when data is received.
77 * A TransportStream is created when needed in order to transport the data over
78 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
79 * on the negotiated SDP's between the peers based on the bundle and rtcp
80 * configuration. Some cases are outlined below for a simple single
81 * audio/video/data session:
83 * - max-bundle uses a single transport for all
84 * media/data transported. Renegotiation involves adding/removing the
85 * necessary streams to the existing transports.
86 * - max-compat involves two TransportStream per media stream
87 * to transport the rtp and the rtcp packets and a single TransportStream for
88 * all data channels. Each stream change involves modifying the associated
89 * TransportStream/s as necessary.
94 * assert sending payload type matches the stream
95 * reconfiguration (of anything)
97 * balanced bundle policy
98 * setting custom DTLS certificates
100 * separate session id's from mlineindex properly
101 * how to deal with replacing a input/output track/stream
104 static void _update_need_negotiation (GstWebRTCBin * webrtc);
106 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
107 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
109 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
112 GST_STATIC_CAPS ("application/x-rtp"));
114 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
117 GST_STATIC_CAPS ("application/x-rtp"));
121 PROP_PAD_TRANSCEIVER = 1,
125 _have_nice_elements (GstWebRTCBin * webrtc)
127 GstPluginFeature *feature;
129 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
131 gst_object_unref (feature);
133 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
134 ("%s", "libnice elements are not available"));
138 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
140 gst_object_unref (feature);
142 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
143 ("%s", "libnice elements are not available"));
151 _have_sctp_elements (GstWebRTCBin * webrtc)
153 GstPluginFeature *feature;
155 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
157 gst_object_unref (feature);
159 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
160 ("%s", "sctp elements are not available"));
164 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
166 gst_object_unref (feature);
168 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
169 ("%s", "sctp elements are not available"));
177 _have_dtls_elements (GstWebRTCBin * webrtc)
179 GstPluginFeature *feature;
181 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
183 gst_object_unref (feature);
185 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
186 ("%s", "dtls elements are not available"));
190 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
192 gst_object_unref (feature);
194 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
195 ("%s", "dtls elements are not available"));
202 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
205 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
206 GValue * value, GParamSpec * pspec)
208 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
211 case PROP_PAD_TRANSCEIVER:
212 g_value_set_object (value, pad->trans);
215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
221 gst_webrtc_bin_pad_finalize (GObject * object)
223 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
226 gst_object_unref (pad->trans);
229 if (pad->received_caps)
230 gst_caps_unref (pad->received_caps);
231 pad->received_caps = NULL;
233 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
237 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
239 GObjectClass *gobject_class = (GObjectClass *) klass;
241 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
242 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
244 g_object_class_install_property (gobject_class,
245 PROP_PAD_TRANSCEIVER,
246 g_param_spec_object ("transceiver", "Transceiver",
247 "Transceiver associated with this pad",
248 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
249 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
253 gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
255 if (wpad->received_caps) {
256 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
257 GstPad *pad = GST_PAD (wpad);
260 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
261 gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
262 trans->current_ssrc, NULL));
263 gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
268 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
270 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
271 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
272 gboolean check_negotiation = FALSE;
274 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
277 gst_event_parse_caps (event, &caps);
278 check_negotiation = (!wpad->received_caps
279 || gst_caps_is_equal (wpad->received_caps, caps));
280 gst_caps_replace (&wpad->received_caps, caps);
282 GST_DEBUG_OBJECT (parent,
283 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
284 GST_PTR_FORMAT, pad, check_negotiation, caps);
286 if (check_negotiation) {
287 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
288 const GstStructure *s;
290 s = gst_caps_get_structure (caps, 0);
291 gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
292 gst_webrtc_bin_pad_update_ssrc_event (wpad);
294 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
295 check_negotiation = TRUE;
298 if (check_negotiation) {
300 _update_need_negotiation (webrtc);
304 return gst_pad_event_default (pad, parent, event);
308 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
312 static GstWebRTCBinPad *
313 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
315 GstWebRTCBinPad *pad;
316 GstPadTemplate *template;
318 if (direction == GST_PAD_SINK)
319 template = gst_static_pad_template_get (&sink_template);
320 else if (direction == GST_PAD_SRC)
321 template = gst_static_pad_template_get (&src_template);
323 g_assert_not_reached ();
326 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
327 direction, "template", template, NULL);
328 gst_object_unref (template);
331 /* FIXME: This can be removed after merging the patch below.
332 * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/540
334 if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
335 gst_object_unref (pad);
339 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
341 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
342 direction == GST_PAD_SRC ? "src" : "sink");
346 #define gst_webrtc_bin_parent_class parent_class
347 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
348 G_ADD_PRIVATE (GstWebRTCBin)
349 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
350 "webrtcbin element"););
352 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
353 GstWebRTCBinPad * pad);
359 CREATE_ANSWER_SIGNAL,
360 SET_LOCAL_DESCRIPTION_SIGNAL,
361 SET_REMOTE_DESCRIPTION_SIGNAL,
362 ADD_ICE_CANDIDATE_SIGNAL,
363 ON_NEGOTIATION_NEEDED_SIGNAL,
364 ON_ICE_CANDIDATE_SIGNAL,
365 ON_NEW_TRANSCEIVER_SIGNAL,
367 ADD_TRANSCEIVER_SIGNAL,
368 GET_TRANSCEIVER_SIGNAL,
369 GET_TRANSCEIVERS_SIGNAL,
370 ADD_TURN_SERVER_SIGNAL,
371 CREATE_DATA_CHANNEL_SIGNAL,
372 ON_DATA_CHANNEL_SIGNAL,
379 PROP_CONNECTION_STATE,
380 PROP_SIGNALING_STATE,
381 PROP_ICE_GATHERING_STATE,
382 PROP_ICE_CONNECTION_STATE,
383 PROP_LOCAL_DESCRIPTION,
384 PROP_CURRENT_LOCAL_DESCRIPTION,
385 PROP_PENDING_LOCAL_DESCRIPTION,
386 PROP_REMOTE_DESCRIPTION,
387 PROP_CURRENT_REMOTE_DESCRIPTION,
388 PROP_PENDING_REMOTE_DESCRIPTION,
392 PROP_ICE_TRANSPORT_POLICY,
394 #ifndef TIZEN_FEATURE_IMPORT_NETSIM
399 PROP_DROP_PROBABILITY_SENDER
403 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
408 GstWebRTCICEStream *stream;
411 /* FIXME: locking? */
413 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
417 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
418 IceStreamItem *item =
419 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
421 if (item->session_id == session_id) {
422 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
423 "session %u", item->stream, session_id);
428 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
434 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
435 GstWebRTCICEStream * stream)
437 IceStreamItem item = { session_id, stream };
439 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
440 "session %u", stream, session_id);
441 g_array_append_val (webrtc->priv->ice_stream_map, item);
451 clear_session_mid_item (SessionMidItem * item)
456 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
459 static GstWebRTCRTPTransceiver *
460 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
461 FindTransceiverFunc func)
465 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
466 GstWebRTCRTPTransceiver *transceiver =
467 g_ptr_array_index (webrtc->priv->transceivers, i);
469 if (func (transceiver, data))
477 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
479 return g_strcmp0 (trans->mid, mid) == 0;
483 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
485 return trans->mline == *mline;
488 static GstWebRTCRTPTransceiver *
489 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
491 GstWebRTCRTPTransceiver *trans;
493 trans = _find_transceiver (webrtc, &mlineindex,
494 (FindTransceiverFunc) transceiver_match_for_mline);
496 GST_TRACE_OBJECT (webrtc,
497 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
503 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
506 static TransportStream *
507 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
508 FindTransportFunc func)
512 for (i = 0; i < webrtc->priv->transports->len; i++) {
513 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
515 if (func (stream, data))
523 match_stream_for_session (TransportStream * trans, guint * session)
525 return trans->session_id == *session;
528 static TransportStream *
529 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
531 TransportStream *stream;
533 stream = _find_transport (webrtc, &session_id,
534 (FindTransportFunc) match_stream_for_session);
536 GST_TRACE_OBJECT (webrtc,
537 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
542 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
544 static GstWebRTCBinPad *
545 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
547 GstElement *element = GST_ELEMENT (webrtc);
550 GST_OBJECT_LOCK (webrtc);
552 for (; l; l = g_list_next (l)) {
553 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
555 if (func (l->data, data)) {
556 gst_object_ref (l->data);
557 GST_OBJECT_UNLOCK (webrtc);
562 l = webrtc->priv->pending_pads;
563 for (; l; l = g_list_next (l)) {
564 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
566 if (func (l->data, data)) {
567 gst_object_ref (l->data);
568 GST_OBJECT_UNLOCK (webrtc);
572 GST_OBJECT_UNLOCK (webrtc);
577 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
580 static WebRTCDataChannel *
581 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
582 FindDataChannelFunc func)
586 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
587 WebRTCDataChannel *channel =
588 g_ptr_array_index (webrtc->priv->data_channels, i);
590 if (func (channel, data))
598 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
600 return channel->parent.id == *id;
603 static WebRTCDataChannel *
604 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
606 WebRTCDataChannel *channel;
608 channel = _find_data_channel (webrtc, &id,
609 (FindDataChannelFunc) data_channel_match_for_id);
611 GST_TRACE_OBJECT (webrtc,
612 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
618 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
620 GST_OBJECT_LOCK (webrtc);
621 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
622 GST_OBJECT_UNLOCK (webrtc);
626 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
628 GST_OBJECT_LOCK (webrtc);
629 webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
630 GST_OBJECT_UNLOCK (webrtc);
634 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
636 _remove_pending_pad (webrtc, pad);
638 if (webrtc->priv->running)
639 gst_pad_set_active (GST_PAD (pad), TRUE);
640 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
644 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
646 _remove_pending_pad (webrtc, pad);
648 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
650 GST_OBJECT_LOCK (webrtc);
651 webrtc->priv->max_sink_pad_serial--;
652 GST_OBJECT_UNLOCK (webrtc);
658 GstPadDirection direction;
663 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
665 return GST_PAD_DIRECTION (pad) == match->direction
666 && pad->mlineindex == match->mlineindex;
669 static GstWebRTCBinPad *
670 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
673 MLineMatch m = { direction, mlineindex };
675 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
680 GstPadDirection direction;
681 GstWebRTCRTPTransceiver *trans;
685 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
687 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
690 static GstWebRTCBinPad *
691 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
692 GstWebRTCRTPTransceiver * trans)
694 TransMatch m = { direction, trans };
696 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
701 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
703 return pad->ssrc == *ssrc;
707 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
714 _unlock_pc_thread (GMutex * lock)
716 g_mutex_unlock (lock);
717 return G_SOURCE_REMOVE;
721 _gst_pc_thread (GstWebRTCBin * webrtc)
724 webrtc->priv->main_context = g_main_context_new ();
725 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
727 PC_COND_BROADCAST (webrtc);
728 g_main_context_invoke (webrtc->priv->main_context,
729 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
731 /* Having the thread be the thread default GMainContext will break the
732 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
734 g_main_loop_run (webrtc->priv->loop);
736 GST_OBJECT_LOCK (webrtc);
737 g_main_context_unref (webrtc->priv->main_context);
738 webrtc->priv->main_context = NULL;
739 GST_OBJECT_UNLOCK (webrtc);
742 g_main_loop_unref (webrtc->priv->loop);
743 webrtc->priv->loop = NULL;
744 PC_COND_BROADCAST (webrtc);
751 _start_thread (GstWebRTCBin * webrtc)
756 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
757 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
761 while (!webrtc->priv->loop)
762 PC_COND_WAIT (webrtc);
763 webrtc->priv->is_closed = FALSE;
768 _stop_thread (GstWebRTCBin * webrtc)
770 GST_OBJECT_LOCK (webrtc);
771 webrtc->priv->is_closed = TRUE;
772 GST_OBJECT_UNLOCK (webrtc);
775 g_main_loop_quit (webrtc->priv->loop);
776 while (webrtc->priv->loop)
777 PC_COND_WAIT (webrtc);
780 g_thread_unref (webrtc->priv->thread);
784 _execute_op (GstWebRTCBinTask * op)
786 PC_LOCK (op->webrtc);
787 if (op->webrtc->priv->is_closed) {
790 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
791 "webrtcbin is closed. aborting execution.");
793 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
794 "error", G_TYPE_ERROR, error, NULL);
796 gst_promise_reply (op->promise, s);
798 g_clear_error (&error);
800 GST_DEBUG_OBJECT (op->webrtc,
801 "Peerconnection is closed, aborting execution");
805 op->op (op->webrtc, op->data);
808 PC_UNLOCK (op->webrtc);
809 return G_SOURCE_REMOVE;
813 _free_op (GstWebRTCBinTask * op)
816 op->notify (op->data);
818 gst_promise_unref (op->promise);
823 * @promise is for correctly signalling the failure case to the caller when
824 * the user supplies it. Without passing it in, the promise would never
825 * be replied to in the case that @webrtc becomes closed between the idle
826 * source addition and the the execution of the idle source.
829 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
830 gpointer data, GDestroyNotify notify, GstPromise * promise)
832 GstWebRTCBinTask *op;
836 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
838 GST_OBJECT_LOCK (webrtc);
839 if (webrtc->priv->is_closed) {
840 GST_OBJECT_UNLOCK (webrtc);
841 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
846 ctx = g_main_context_ref (webrtc->priv->main_context);
847 GST_OBJECT_UNLOCK (webrtc);
849 op = g_new0 (GstWebRTCBinTask, 1);
855 op->promise = gst_promise_ref (promise);
857 source = g_idle_source_new ();
858 g_source_set_priority (source, G_PRIORITY_DEFAULT);
859 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
860 (GDestroyNotify) _free_op);
861 g_source_attach (source, ctx);
862 g_source_unref (source);
863 g_main_context_unref (ctx);
868 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
869 static GstWebRTCICEConnectionState
870 _collate_ice_connection_states (GstWebRTCBin * webrtc)
872 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
873 GstWebRTCICEConnectionState any_state = 0;
874 gboolean all_new_or_closed = TRUE;
875 gboolean all_completed_or_closed = TRUE;
876 gboolean all_connected_completed_or_closed = TRUE;
879 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
880 GstWebRTCRTPTransceiver *rtp_trans =
881 g_ptr_array_index (webrtc->priv->transceivers, i);
882 GstWebRTCICETransport *transport;
883 GstWebRTCICEConnectionState ice_state;
885 if (rtp_trans->stopped) {
886 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
890 if (!rtp_trans->mid) {
891 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
895 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
897 /* get transport state */
898 g_object_get (transport, "state", &ice_state, NULL);
899 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
901 any_state |= (1 << ice_state);
903 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
904 all_new_or_closed = FALSE;
905 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
906 all_completed_or_closed = FALSE;
907 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
908 && ice_state != STATE (CLOSED))
909 all_connected_completed_or_closed = FALSE;
912 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
914 if (webrtc->priv->is_closed) {
915 GST_TRACE_OBJECT (webrtc, "returning closed");
916 return STATE (CLOSED);
918 /* Any of the RTCIceTransports are in the failed state. */
919 if (any_state & (1 << STATE (FAILED))) {
920 GST_TRACE_OBJECT (webrtc, "returning failed");
921 return STATE (FAILED);
923 /* Any of the RTCIceTransports are in the disconnected state. */
924 if (any_state & (1 << STATE (DISCONNECTED))) {
925 GST_TRACE_OBJECT (webrtc, "returning disconnected");
926 return STATE (DISCONNECTED);
928 /* All of the RTCIceTransports are in the new or closed state, or there are
930 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
931 GST_TRACE_OBJECT (webrtc, "returning new");
934 /* Any of the RTCIceTransports are in the checking or new state. */
935 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
936 GST_TRACE_OBJECT (webrtc, "returning checking");
937 return STATE (CHECKING);
939 /* All RTCIceTransports are in the completed or closed state. */
940 if (all_completed_or_closed) {
941 GST_TRACE_OBJECT (webrtc, "returning completed");
942 return STATE (COMPLETED);
944 /* All RTCIceTransports are in the connected, completed or closed state. */
945 if (all_connected_completed_or_closed) {
946 GST_TRACE_OBJECT (webrtc, "returning connected");
947 return STATE (CONNECTED);
950 GST_FIXME ("unspecified situation, returning old state");
951 return webrtc->ice_connection_state;
955 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
956 static GstWebRTCICEGatheringState
957 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
959 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
960 GstWebRTCICEGatheringState any_state = 0;
961 gboolean all_completed = webrtc->priv->transceivers->len > 0;
964 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
965 GstWebRTCRTPTransceiver *rtp_trans =
966 g_ptr_array_index (webrtc->priv->transceivers, i);
967 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
968 TransportStream *stream = trans->stream;
969 GstWebRTCDTLSTransport *dtls_transport;
970 GstWebRTCICETransport *transport;
971 GstWebRTCICEGatheringState ice_state;
973 if (rtp_trans->stopped || stream == NULL) {
974 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
979 /* We only have a mid in the transceiver after we got the SDP answer,
980 * which is usually long after gathering has finished */
981 if (!rtp_trans->mid) {
982 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
985 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
986 if (dtls_transport == NULL) {
987 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
991 transport = dtls_transport->transport;
993 /* get gathering state */
994 g_object_get (transport, "gathering-state", &ice_state, NULL);
995 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
997 any_state |= (1 << ice_state);
998 if (ice_state != STATE (COMPLETE))
999 all_completed = FALSE;
1002 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1004 /* Any of the RTCIceTransport s are in the gathering state. */
1005 if (any_state & (1 << STATE (GATHERING))) {
1006 GST_TRACE_OBJECT (webrtc, "returning gathering");
1007 return STATE (GATHERING);
1009 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1010 * the completed gathering state. */
1011 if (all_completed) {
1012 GST_TRACE_OBJECT (webrtc, "returning complete");
1013 return STATE (COMPLETE);
1016 /* Any of the RTCIceTransport s are in the new gathering state and none
1017 * of the transports are in the gathering state, or there are no transports. */
1018 GST_TRACE_OBJECT (webrtc, "returning new");
1023 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1024 static GstWebRTCPeerConnectionState
1025 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1027 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1028 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1029 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1030 GstWebRTCICEConnectionState any_ice_state = 0;
1031 GstWebRTCDTLSTransportState any_dtls_state = 0;
1032 gboolean ice_all_new_or_closed = TRUE;
1033 gboolean dtls_all_new_or_closed = TRUE;
1034 gboolean ice_all_new_connecting_or_checking = TRUE;
1035 gboolean dtls_all_new_connecting_or_checking = TRUE;
1036 gboolean ice_all_connected_completed_or_closed = TRUE;
1037 gboolean dtls_all_connected_completed_or_closed = TRUE;
1040 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1041 GstWebRTCRTPTransceiver *rtp_trans =
1042 g_ptr_array_index (webrtc->priv->transceivers, i);
1043 GstWebRTCDTLSTransport *transport;
1044 GstWebRTCICEConnectionState ice_state;
1045 GstWebRTCDTLSTransportState dtls_state;
1047 if (rtp_trans->stopped) {
1048 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1051 if (!rtp_trans->mid) {
1052 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1056 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1058 /* get transport state */
1059 g_object_get (transport, "state", &dtls_state, NULL);
1060 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1062 any_dtls_state |= (1 << dtls_state);
1064 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1065 dtls_all_new_or_closed = FALSE;
1066 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1067 dtls_all_new_connecting_or_checking = FALSE;
1068 if (dtls_state != DTLS_STATE (CONNECTED)
1069 && dtls_state != DTLS_STATE (CLOSED))
1070 dtls_all_connected_completed_or_closed = FALSE;
1072 g_object_get (transport->transport, "state", &ice_state, NULL);
1073 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1075 any_ice_state |= (1 << ice_state);
1077 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1078 ice_all_new_or_closed = FALSE;
1079 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1080 ice_all_new_connecting_or_checking = FALSE;
1081 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1082 && ice_state != ICE_STATE (CLOSED))
1083 ice_all_connected_completed_or_closed = FALSE;
1086 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1087 "state: 0x%x", any_ice_state, any_dtls_state);
1089 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1090 if (webrtc->priv->is_closed) {
1091 GST_TRACE_OBJECT (webrtc, "returning closed");
1092 return STATE (CLOSED);
1095 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1096 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1097 GST_TRACE_OBJECT (webrtc, "returning failed");
1098 return STATE (FAILED);
1100 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1101 GST_TRACE_OBJECT (webrtc, "returning failed");
1102 return STATE (FAILED);
1105 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1107 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1108 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1109 return STATE (DISCONNECTED);
1112 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1113 * state, or there are no transports. */
1114 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1115 || webrtc->priv->transceivers->len == 0) {
1116 GST_TRACE_OBJECT (webrtc, "returning new");
1120 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1121 * or checking state. */
1122 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1123 GST_TRACE_OBJECT (webrtc, "returning connecting");
1124 return STATE (CONNECTING);
1127 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1128 * completed or closed state. */
1129 if (dtls_all_connected_completed_or_closed
1130 && ice_all_connected_completed_or_closed) {
1131 GST_TRACE_OBJECT (webrtc, "returning connected");
1132 return STATE (CONNECTED);
1135 /* FIXME: Unspecified state that happens for us */
1136 if ((dtls_all_new_connecting_or_checking
1137 || dtls_all_connected_completed_or_closed)
1138 && (ice_all_new_connecting_or_checking
1139 || ice_all_connected_completed_or_closed)) {
1140 GST_TRACE_OBJECT (webrtc, "returning connecting");
1141 return STATE (CONNECTING);
1144 GST_FIXME_OBJECT (webrtc,
1145 "Undefined situation detected, returning old state");
1146 return webrtc->peer_connection_state;
1154 _update_and_notify_ice_gathering_state (GstWebRTCBin * webrtc, GstWebRTCICEGatheringState state)
1156 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1158 if (state != webrtc->ice_gathering_state) {
1159 gchar *old_s, *new_s;
1161 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1163 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1165 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1166 old_s, old_state, new_s, state);
1170 webrtc->ice_gathering_state = state;
1172 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1179 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1181 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1182 GstWebRTCICEGatheringState new_state;
1184 new_state = _collate_ice_gathering_states (webrtc);
1186 /* If the new state is complete, before we update the public state,
1187 * check if anyone published more ICE candidates while we were collating
1188 * and stop if so, because it means there's a new later
1189 * ice_gathering_state_task queued */
1190 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1192 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1193 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1195 webrtc->pending_ice_gathering_state = GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE;
1196 GST_INFO_OBJECT (webrtc, "set pending_ice_gathering_state to (%u)",
1197 webrtc->pending_ice_gathering_state);
1198 ICE_UNLOCK (webrtc);
1202 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1205 ICE_UNLOCK (webrtc);
1209 _update_and_notify_ice_gathering_state (webrtc, new_state);
1211 if (new_state != webrtc->ice_gathering_state) {
1212 gchar *old_s, *new_s;
1214 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1216 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1218 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1219 old_s, old_state, new_s, new_state);
1223 webrtc->ice_gathering_state = new_state;
1225 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1232 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1234 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1239 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1241 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1242 GstWebRTCICEConnectionState new_state;
1244 new_state = _collate_ice_connection_states (webrtc);
1246 if (new_state != old_state) {
1247 gchar *old_s, *new_s;
1249 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1251 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1253 GST_INFO_OBJECT (webrtc,
1254 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1259 webrtc->ice_connection_state = new_state;
1261 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1267 _update_ice_connection_state (GstWebRTCBin * webrtc)
1269 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1274 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1276 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1277 GstWebRTCPeerConnectionState new_state;
1279 new_state = _collate_peer_connection_states (webrtc);
1281 if (new_state != old_state) {
1282 gchar *old_s, *new_s;
1284 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1286 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1288 GST_INFO_OBJECT (webrtc,
1289 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1294 webrtc->peer_connection_state = new_state;
1296 g_object_notify (G_OBJECT (webrtc), "connection-state");
1302 _update_peer_connection_state (GstWebRTCBin * webrtc)
1304 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1309 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1312 gboolean res = FALSE;
1314 GST_OBJECT_LOCK (webrtc);
1315 l = GST_ELEMENT (webrtc)->pads;
1316 for (; l; l = g_list_next (l)) {
1317 GstWebRTCBinPad *wpad;
1319 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1322 wpad = GST_WEBRTC_BIN_PAD (l->data);
1323 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1324 && (!wpad->trans || !wpad->trans->stopped)) {
1329 l = webrtc->priv->pending_pads;
1330 for (; l; l = g_list_next (l)) {
1331 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1339 GST_OBJECT_UNLOCK (webrtc);
1343 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1345 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1349 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1351 /* We can't negotiate until we have received caps on all our sink pads,
1352 * as we will need the ssrcs in our offer / answer */
1353 if (!_all_sinks_have_caps (webrtc)) {
1354 GST_LOG_OBJECT (webrtc,
1355 "no negotiation possible until caps have been received on all sink pads");
1359 /* If any implementation-specific negotiation is required, as described at
1360 * the start of this section, return "true".
1362 /* FIXME: emit when input caps/format changes? */
1364 if (!webrtc->current_local_description) {
1365 GST_LOG_OBJECT (webrtc, "no local description set");
1369 if (!webrtc->current_remote_description) {
1370 GST_LOG_OBJECT (webrtc, "no remote description set");
1374 /* If connection has created any RTCDataChannel's, and no m= section has
1375 * been negotiated yet for data, return "true". */
1376 if (webrtc->priv->data_channels->len > 0) {
1377 if (_message_get_datachannel_index (webrtc->current_local_description->
1378 sdp) >= G_MAXUINT) {
1379 GST_LOG_OBJECT (webrtc,
1380 "no data channel media section and have %u " "transports",
1381 webrtc->priv->data_channels->len);
1386 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1387 GstWebRTCRTPTransceiver *trans;
1389 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1391 if (trans->stopped) {
1392 /* FIXME: If t is stopped and is associated with an m= section according to
1393 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1394 * rejected in connection's currentLocalDescription or
1395 * currentRemoteDescription , return "true". */
1396 GST_FIXME_OBJECT (webrtc,
1397 "check if the transceiver is rejected in descriptions");
1399 const GstSDPMedia *media;
1400 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1402 if (trans->mline == -1 || trans->mid == NULL) {
1403 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1404 " mid %s", i, trans, trans->mid);
1407 /* internal inconsistency */
1408 g_assert (trans->mline <
1409 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1410 g_assert (trans->mline <
1411 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1413 /* FIXME: msid handling
1414 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1415 * section in connection's currentLocalDescription doesn't contain an
1416 * "a=msid" line, return "true". */
1419 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1421 local_dir = _get_direction_from_media (media);
1424 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1426 remote_dir = _get_direction_from_media (media);
1428 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1429 /* If connection's currentLocalDescription if of type "offer", and
1430 * the direction of the associated m= section in neither the offer
1431 * nor answer matches t's direction, return "true". */
1433 if (local_dir != trans->direction && remote_dir != trans->direction) {
1434 gchar *local_str, *remote_str, *dir_str;
1437 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1440 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1443 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1446 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1447 "description (local %s remote %s)", dir_str, local_str,
1452 g_free (remote_str);
1456 } else if (webrtc->current_local_description->type ==
1457 GST_WEBRTC_SDP_TYPE_ANSWER) {
1458 GstWebRTCRTPTransceiverDirection intersect_dir;
1460 /* If connection's currentLocalDescription if of type "answer", and
1461 * the direction of the associated m= section in the answer does not
1462 * match t's direction intersected with the offered direction (as
1463 * described in [JSEP] (section 5.3.1.)), return "true". */
1465 /* remote is the offer, local is the answer */
1466 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1468 if (intersect_dir != trans->direction) {
1469 gchar *local_str, *remote_str, *inter_str, *dir_str;
1472 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1475 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1478 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1481 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1484 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1485 "description intersected direction %s (local %s remote %s)",
1486 dir_str, local_str, inter_str, remote_str);
1490 g_free (remote_str);
1499 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1504 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1506 if (webrtc->priv->need_negotiation) {
1507 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1509 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1515 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1517 _update_need_negotiation (GstWebRTCBin * webrtc)
1519 /* If connection's [[isClosed]] slot is true, abort these steps. */
1520 if (webrtc->priv->is_closed)
1522 /* If connection's signaling state is not "stable", abort these steps. */
1523 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1526 /* If the result of checking if negotiation is needed is "false", clear the
1527 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1528 * to false, and abort these steps. */
1529 if (!_check_if_negotiation_is_needed (webrtc)) {
1530 webrtc->priv->need_negotiation = FALSE;
1533 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1534 if (webrtc->priv->need_negotiation)
1536 /* Set connection's [[needNegotiation]] slot to true. */
1537 webrtc->priv->need_negotiation = TRUE;
1538 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1539 * true, fire a simple event named negotiationneeded at connection. */
1540 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1545 _find_codec_preferences (GstWebRTCBin * webrtc,
1546 GstWebRTCRTPTransceiver * rtp_trans, GstPadDirection direction,
1549 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1550 GstCaps *ret = NULL;
1552 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1555 if (rtp_trans && rtp_trans->codec_preferences) {
1556 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1557 rtp_trans->codec_preferences);
1558 ret = gst_caps_ref (rtp_trans->codec_preferences);
1560 GstWebRTCBinPad *pad = NULL;
1562 /* try to find a pad */
1564 || !(pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans)))
1565 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1568 if (trans && trans->last_configured_caps)
1569 ret = gst_caps_ref (trans->last_configured_caps);
1571 GstCaps *caps = NULL;
1573 if (pad->received_caps) {
1574 caps = gst_caps_ref (pad->received_caps);
1575 } else if ((caps = gst_pad_get_current_caps (GST_PAD (pad)))) {
1576 GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1579 if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
1580 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1585 gst_caps_replace (&trans->last_configured_caps, caps);
1590 gst_object_unref (pad);
1595 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1601 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1602 WebRTCTransceiver * trans, const GstCaps * caps)
1607 ret = gst_caps_make_writable (caps);
1609 for (i = 0; i < gst_caps_get_size (ret); i++) {
1610 GstStructure *s = gst_caps_get_structure (ret, i);
1613 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1614 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1616 if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1617 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1618 /* FIXME: is this needed? */
1619 /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1620 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1622 /* FIXME: codec-specific parameters? */
1629 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1630 GParamSpec * pspec, GstWebRTCBin * webrtc)
1632 _update_ice_connection_state (webrtc);
1633 _update_peer_connection_state (webrtc);
1637 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1638 GParamSpec * pspec, GstWebRTCBin * webrtc)
1640 _update_ice_gathering_state (webrtc);
1644 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1645 GParamSpec * pspec, GstWebRTCBin * webrtc)
1647 _update_peer_connection_state (webrtc);
1651 match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
1653 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1655 return (trans->current_ssrc == GPOINTER_TO_UINT (data));
1659 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
1660 gboolean early, gpointer user_data)
1662 GstWebRTCBin *webrtc = user_data;
1663 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
1664 GstRTCPPacket packet;
1666 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
1669 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
1670 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
1672 GstWebRTCRTPTransceiver *rtp_trans;
1673 WebRTCTransceiver *trans;
1675 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
1678 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1680 trans = (WebRTCTransceiver *) rtp_trans;
1682 if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
1684 gchar *pad_name = NULL;
1687 g_strdup_printf ("send_rtcp_src_%u",
1688 rtp_trans->sender->transport->session_id);
1689 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
1692 gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
1693 gst_object_unref (pad);
1699 gst_rtcp_buffer_unmap (&rtcp);
1702 /* False means we don't care about suppression */
1707 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
1709 GObject *internal_session = NULL;
1711 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
1712 session_id, &internal_session);
1714 if (internal_session) {
1715 g_signal_connect (internal_session, "on-sending-rtcp",
1716 G_CALLBACK (_on_sending_rtcp), webrtc);
1717 g_object_unref (internal_session);
1721 static GstPadProbeReturn
1722 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1724 GstWebRTCBin *webrtc = user_data;
1726 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
1727 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
1728 const GstStructure *s =
1729 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
1731 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
1735 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1736 GstWebRTCRTPTransceiver *rtp_trans;
1738 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1741 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1742 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
1743 trans->stream->session_id);
1746 /* Set DSCP field based on
1747 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
1749 switch (rtp_trans->sender->priority) {
1750 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
1753 case GST_WEBRTC_PRIORITY_TYPE_LOW:
1756 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
1757 switch (rtp_trans->kind) {
1758 case GST_WEBRTC_KIND_AUDIO:
1761 case GST_WEBRTC_KIND_VIDEO:
1762 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
1764 case GST_WEBRTC_KIND_UNKNOWN:
1769 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
1770 switch (rtp_trans->kind) {
1771 case GST_WEBRTC_KIND_AUDIO:
1774 case GST_WEBRTC_KIND_VIDEO:
1775 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
1777 case GST_WEBRTC_KIND_UNKNOWN:
1784 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
1786 } else if (gst_structure_get_enum (s, "sctp-priority",
1787 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
1790 /* Set DSCP field based on
1791 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
1794 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
1797 case GST_WEBRTC_PRIORITY_TYPE_LOW:
1800 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
1801 dscp = 10; /* AF11 */
1803 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
1804 dscp = 18; /* AF21 */
1807 if (webrtc->priv->data_channel_transport)
1808 gst_webrtc_ice_set_tos (webrtc->priv->ice,
1809 webrtc->priv->data_channel_transport->stream, dscp << 2);
1813 return GST_PAD_PROBE_OK;
1816 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
1819 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
1821 GstWebRTCPriorityType sctp_priority = 0;
1824 if (!webrtc->priv->sctp_transport)
1827 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1828 GstWebRTCDataChannel *channel
1829 = g_ptr_array_index (webrtc->priv->data_channels, i);
1831 sctp_priority = MAX (sctp_priority, channel->priority);
1834 /* Default priority is low means DSCP field is left as 0 */
1835 if (sctp_priority == 0)
1836 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
1838 /* Nobody asks for DSCP, leave it as-is */
1839 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
1840 !webrtc->priv->tos_attached)
1843 /* If one stream has a non-default priority, then everyone else does too */
1844 gst_webrtc_bin_attach_tos (webrtc);
1846 gst_webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
1851 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
1852 GstWebRTCICETransport * transport)
1856 pad = gst_element_get_static_pad (transport->sink, "sink");
1857 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1858 _nicesink_pad_probe, g_object_ref (webrtc),
1859 (GDestroyNotify) gst_object_unref);
1860 gst_object_unref (pad);
1864 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
1868 if (webrtc->priv->tos_attached)
1870 webrtc->priv->tos_attached = TRUE;
1872 for (i = 0; i < webrtc->priv->transports->len; i++) {
1873 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
1875 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
1877 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
1878 stream->transport->transport);
1881 gst_webrtc_bin_update_sctp_priority (webrtc);
1884 static WebRTCTransceiver *
1885 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
1886 GstWebRTCRTPTransceiverDirection direction, guint mline)
1888 WebRTCTransceiver *trans;
1889 GstWebRTCRTPTransceiver *rtp_trans;
1890 GstWebRTCRTPSender *sender;
1891 GstWebRTCRTPReceiver *receiver;
1893 sender = gst_webrtc_rtp_sender_new ();
1894 receiver = gst_webrtc_rtp_receiver_new ();
1895 trans = webrtc_transceiver_new (webrtc, sender, receiver);
1896 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1897 rtp_trans->direction = direction;
1898 rtp_trans->mline = mline;
1899 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
1900 rtp_trans->stopped = FALSE;
1902 g_signal_connect_object (sender, "notify::priority",
1903 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
1905 g_ptr_array_add (webrtc->priv->transceivers, trans);
1907 gst_object_unref (sender);
1908 gst_object_unref (receiver);
1910 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
1918 _remove_webrtc_transceiver (GstWebRTCBin * webrtc,
1919 GstWebRTCRTPTransceiver * trans)
1921 g_ptr_array_remove (webrtc->priv->transceivers, trans);
1922 gst_object_unref (trans);
1926 static TransportStream *
1927 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1929 GstWebRTCDTLSTransport *transport;
1930 TransportStream *ret;
1932 /* FIXME: how to parametrize the sender and the receiver */
1933 ret = transport_stream_new (webrtc, session_id);
1934 transport = ret->transport;
1936 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1937 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1938 g_signal_connect (G_OBJECT (transport->transport),
1939 "notify::gathering-state",
1940 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1941 g_signal_connect (G_OBJECT (transport), "notify::state",
1942 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1943 if (webrtc->priv->tos_attached)
1944 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
1946 GST_TRACE_OBJECT (webrtc,
1947 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1952 static TransportStream *
1953 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1955 TransportStream *ret;
1958 ret = _find_transport_for_session (webrtc, session_id);
1961 ret = _create_transport_channel (webrtc, session_id);
1962 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1963 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1964 g_ptr_array_add (webrtc->priv->transports, ret);
1966 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1967 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1968 GST_ELEMENT (webrtc->rtpbin), pad_name))
1969 g_warn_if_reached ();
1972 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1973 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1974 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1975 g_warn_if_reached ();
1976 if (webrtc->priv->tos_attached)
1977 gst_webrtc_bin_attach_tos_to_session (webrtc, ret->session_id);
1981 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1982 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1987 /* this is called from the webrtc thread with the pc lock held */
1989 _on_data_channel_ready_state (WebRTCDataChannel * channel,
1990 GParamSpec * pspec, GstWebRTCBin * webrtc)
1992 GstWebRTCDataChannelState ready_state;
1995 g_object_get (channel, "ready-state", &ready_state, NULL);
1997 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
1998 gboolean found = FALSE;
2000 for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) {
2001 WebRTCDataChannel *c;
2003 c = g_ptr_array_index (webrtc->priv->pending_data_channels, i);
2006 g_ptr_array_remove_index (webrtc->priv->pending_data_channels, i);
2010 if (found == FALSE) {
2011 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2015 g_ptr_array_add (webrtc->priv->data_channels, channel);
2017 gst_webrtc_bin_update_sctp_priority (webrtc);
2019 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2020 gst_object_ref (channel));
2025 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2026 GstWebRTCBin * webrtc)
2028 WebRTCDataChannel *channel;
2032 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2036 channel = _find_data_channel_for_id (webrtc, stream_id);
2038 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2039 channel->parent.id = stream_id;
2040 channel->webrtcbin = webrtc;
2042 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
2043 gst_bin_add (GST_BIN (webrtc), channel->appsink);
2045 gst_element_sync_state_with_parent (channel->appsrc);
2046 gst_element_sync_state_with_parent (channel->appsink);
2048 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2050 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2053 g_signal_connect (channel, "notify::ready-state",
2054 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2056 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2057 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2058 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2059 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2060 gst_object_unref (sink_pad);
2065 _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
2066 GstWebRTCBin * webrtc)
2068 GstWebRTCSCTPTransportState state;
2070 g_object_get (sctp, "state", &state, NULL);
2072 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2076 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2078 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2079 WebRTCDataChannel *channel;
2081 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2083 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2085 if (!channel->parent.negotiated && !channel->opened)
2086 webrtc_data_channel_start_negotiation (channel);
2092 /* Forward declaration so we can easily disconnect the signal handler */
2093 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2094 GParamSpec * pspec, GstWebRTCBin * webrtc);
2097 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2099 TransportStream *stream;
2100 GstWebRTCDTLSTransport *transport;
2101 GstWebRTCDTLSTransportState dtls_state;
2102 GstWebRTCSCTPTransport *sctp_transport;
2104 stream = webrtc->priv->data_channel_transport;
2105 transport = stream->transport;
2107 g_object_get (transport, "state", &dtls_state, NULL);
2108 /* Not connected yet so just return */
2109 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2110 GST_DEBUG_OBJECT (webrtc,
2111 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2115 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2116 sctp_transport = webrtc->priv->sctp_transport;
2118 /* Not locked state anymore so this was already taken care of before */
2119 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2122 /* Start up the SCTP elements now that the DTLS connection is established */
2123 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2124 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2126 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2127 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2129 if (sctp_transport->sctpdec_block_id) {
2130 GstPad *receive_srcpad;
2133 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2135 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2137 sctp_transport->sctpdec_block_id = 0;
2138 gst_object_unref (receive_srcpad);
2141 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2146 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2147 GParamSpec * pspec, GstWebRTCBin * webrtc)
2149 GstWebRTCDTLSTransportState dtls_state;
2151 g_object_get (transport, "state", &dtls_state, NULL);
2153 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2156 /* Connected now, so schedule a task to update the state of the SCTP
2158 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2159 gst_webrtc_bin_enqueue_task (webrtc,
2160 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2164 static GstPadProbeReturn
2165 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2167 /* Drop all events: we don't care about them and don't want to block on
2168 * them. Sticky events would be forwarded again later once we unblock
2169 * and we don't want to forward them here already because that might
2170 * cause a spurious GST_FLOW_FLUSHING */
2171 if (GST_IS_EVENT (info->data))
2172 return GST_PAD_PROBE_DROP;
2174 /* But block on any actual data-flow so we don't accidentally send that
2175 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2178 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2180 return GST_PAD_PROBE_OK;
2183 static TransportStream *
2184 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2186 if (!webrtc->priv->data_channel_transport) {
2187 TransportStream *stream;
2188 GstWebRTCSCTPTransport *sctp_transport;
2191 stream = _find_transport_for_session (webrtc, session_id);
2194 stream = _create_transport_channel (webrtc, session_id);
2195 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
2196 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
2197 g_ptr_array_add (webrtc->priv->transports, stream);
2200 webrtc->priv->data_channel_transport = stream;
2202 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2203 sctp_transport = gst_webrtc_sctp_transport_new ();
2204 sctp_transport->transport =
2205 g_object_ref (webrtc->priv->data_channel_transport->transport);
2206 sctp_transport->webrtcbin = webrtc;
2208 /* Don't automatically start SCTP elements as part of webrtcbin. We
2209 * need to delay this until the DTLS transport is fully connected! */
2210 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2211 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2213 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2214 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2217 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2218 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2219 g_signal_connect (sctp_transport, "notify::state",
2220 G_CALLBACK (_on_sctp_state_notify), webrtc);
2222 if (sctp_transport->sctpdec_block_id == 0) {
2223 GstPad *receive_srcpad;
2225 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2227 sctp_transport->sctpdec_block_id =
2228 gst_pad_add_probe (receive_srcpad,
2229 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2230 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2231 gst_object_unref (receive_srcpad);
2234 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2235 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2236 g_warn_if_reached ();
2238 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2239 GST_ELEMENT (stream->send_bin), "data_sink"))
2240 g_warn_if_reached ();
2242 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2243 WebRTCDataChannel *channel;
2245 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2247 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2250 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2251 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2253 if (!webrtc->priv->sctp_transport) {
2254 /* Connect to the notify::state signal to get notified when the DTLS
2255 * connection is established. Only then can we start the SCTP elements */
2256 g_signal_connect (stream->transport, "notify::state",
2257 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2259 /* As this would be racy otherwise, also schedule a task that checks the
2260 * current state of the connection already without getting the signal
2262 gst_webrtc_bin_enqueue_task (webrtc,
2263 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2266 webrtc->priv->sctp_transport = sctp_transport;
2267 gst_webrtc_bin_update_sctp_priority (webrtc);
2270 return webrtc->priv->data_channel_transport;
2273 static TransportStream *
2274 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2275 gboolean is_datachannel)
2278 return _get_or_create_data_channel_transports (webrtc, session_id);
2280 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2284 g_array_find_uint (GArray * array, guint val)
2288 for (i = 0; i < array->len; i++) {
2289 if (g_array_index (array, guint, i) == val)
2297 _pick_available_pt (GArray * reserved_pts, guint * i)
2299 gboolean ret = FALSE;
2301 for (*i = 96; *i <= 127; (*i)++) {
2302 if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
2303 g_array_append_val (reserved_pts, *i);
2313 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2314 GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
2315 GstSDPMedia * media)
2317 gboolean ret = TRUE;
2319 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2322 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2326 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2329 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2331 str = g_strdup_printf ("%u", pt);
2332 gst_sdp_media_add_format (media, str);
2334 str = g_strdup_printf ("%u red/%d", pt, clockrate);
2335 gst_sdp_media_add_attribute (media, "rtpmap", str);
2338 *rtx_target_pt = pt;
2340 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2343 str = g_strdup_printf ("%u", pt);
2344 gst_sdp_media_add_format (media, str);
2346 str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
2347 gst_sdp_media_add_attribute (media, "rtpmap", str);
2356 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2357 GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
2358 GstSDPMedia * media)
2360 gboolean ret = TRUE;
2362 if (trans->local_rtx_ssrc_map)
2363 gst_structure_free (trans->local_rtx_ssrc_map);
2365 trans->local_rtx_ssrc_map =
2366 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2368 if (trans->do_nack) {
2372 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2375 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2377 str = g_strdup_printf ("%u", target_ssrc);
2378 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2379 g_random_int (), NULL);
2382 str = g_strdup_printf ("%u", pt);
2383 gst_sdp_media_add_format (media, str);
2386 str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
2387 gst_sdp_media_add_attribute (media, "rtpmap", str);
2390 str = g_strdup_printf ("%u apt=%d", pt, target_pt);
2391 gst_sdp_media_add_attribute (media, "fmtp", str);
2399 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2401 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2402 GstSDPMedia * media)
2407 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2408 g_value_get_uint (value));
2409 gst_sdp_media_add_attribute (media, "ssrc-group", str);
2419 GstWebRTCBin *webrtc;
2420 WebRTCTransceiver *trans;
2424 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2430 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2431 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2432 cname = gst_structure_get_string (sdes, "cname");
2434 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2436 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2437 cname, GST_OBJECT_NAME (data->trans));
2438 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2441 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2442 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2445 gst_structure_free (sdes);
2451 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2452 WebRTCTransceiver * trans)
2455 RtxSsrcData data = { media, webrtc, trans };
2459 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2460 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2461 cname = gst_structure_get_string (sdes, "cname");
2463 if (trans->local_rtx_ssrc_map)
2464 gst_structure_foreach (trans->local_rtx_ssrc_map,
2465 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2467 for (i = 0; i < gst_caps_get_size (caps); i++) {
2468 const GstStructure *s = gst_caps_get_structure (caps, i);
2471 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2474 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2476 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2477 GST_OBJECT_NAME (trans));
2478 gst_sdp_media_add_attribute (media, "ssrc", str);
2481 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2482 gst_sdp_media_add_attribute (media, "ssrc", str);
2487 gst_structure_free (sdes);
2489 if (trans->local_rtx_ssrc_map)
2490 gst_structure_foreach (trans->local_rtx_ssrc_map,
2491 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2495 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2496 GstSDPMedia * media)
2498 gchar *cert, *fingerprint, *val;
2500 g_object_get (transport, "certificate", &cert, NULL);
2503 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2506 g_strdup_printf ("%s %s",
2507 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2508 g_free (fingerprint);
2510 gst_sdp_media_add_attribute (media, "fingerprint", val);
2514 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2516 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2517 GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
2518 GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2519 gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids)
2522 * rtp header extensions
2529 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2531 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2532 gchar *direction, *sdp_mid, *ufrag, *pwd;
2533 gboolean bundle_only;
2537 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2538 || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2541 g_assert (trans->mline == -1 || trans->mline == media_idx);
2543 bundle_only = bundled_mids && bundle_idx != media_idx
2544 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2546 /* mandated by JSEP */
2547 gst_sdp_media_add_attribute (media, "setup", "actpass");
2549 /* FIXME: deal with ICE restarts */
2550 if (last_offer && trans->mline != -1 && trans->mid) {
2551 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2552 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2553 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2555 GST_DEBUG_OBJECT (trans,
2556 "%u Generating new ice parameters mline %i, mid %s", media_idx,
2557 trans->mline, trans->mid);
2558 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2559 _generate_ice_credentials (&ufrag, &pwd);
2561 g_assert (bundle_ufrag && bundle_pwd);
2562 ufrag = g_strdup (bundle_ufrag);
2563 pwd = g_strdup (bundle_pwd);
2567 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2568 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2572 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2573 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2574 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2577 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2580 /* FIXME: negotiate this */
2581 /* FIXME: when bundle_only, these should not be added:
2582 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2583 * However, this causes incompatibilities with current versions
2584 * of the major browsers */
2585 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2586 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2589 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2591 gst_sdp_media_add_attribute (media, direction, "");
2594 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2595 caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
2597 _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
2600 g_assert_not_reached ();
2603 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2604 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2606 gst_caps_unref (caps);
2610 for (i = 0; i < gst_caps_get_size (caps); i++) {
2611 GstCaps *format = gst_caps_new_empty ();
2612 const GstStructure *s = gst_caps_get_structure (caps, i);
2614 gst_caps_append_structure (format, gst_structure_copy (s));
2616 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
2617 " to %u-th media", i, format, media_idx);
2619 /* this only looks at the first structure so we loop over the given caps
2620 * and add each structure inside it piecemeal */
2621 gst_sdp_media_set_media_from_caps (format, media);
2623 gst_caps_unref (format);
2626 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2627 const GstStructure *s = gst_caps_get_structure (caps, 0);
2628 gint clockrate = -1;
2630 gint original_rtx_target_pt; /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2631 guint rtx_target_ssrc = -1;
2633 if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
2634 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2635 g_array_append_val (reserved_pts, rtx_target_pt);
2637 original_rtx_target_pt = rtx_target_pt;
2639 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
2640 GST_WARNING_OBJECT (webrtc,
2641 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
2642 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
2643 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
2646 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2647 clockrate, &rtx_target_pt, media);
2648 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2649 clockrate, rtx_target_pt, rtx_target_ssrc, media);
2650 if (original_rtx_target_pt != rtx_target_pt)
2651 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2652 clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
2655 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
2657 /* Some identifier; we also add the media name to it so it's identifiable */
2659 gst_sdp_media_add_attribute (media, "mid", trans->mid);
2661 /* Make sure to avoid mid collisions */
2663 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2664 webrtc->priv->media_counter++);
2665 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
2668 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2669 g_hash_table_insert (all_mids, sdp_mid, NULL);
2676 * - add a=candidate lines for gathered candidates
2679 if (trans->sender) {
2680 if (!trans->sender->transport) {
2681 TransportStream *item;
2684 _get_or_create_transport_stream (webrtc,
2685 bundled_mids ? bundle_idx : media_idx, FALSE);
2687 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
2690 _add_fingerprint_to_media (trans->sender->transport, media);
2694 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2697 g_string_append_printf (bundled_mids, " %s", mid);
2700 gst_caps_unref (caps);
2706 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
2708 if (pad->received_caps) {
2709 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
2712 if (gst_structure_get_int (s, "payload", &pt)) {
2713 GST_TRACE_OBJECT (pad, "have reserved pt %u from received caps", pt);
2714 g_array_append_val (reserved_pts, pt);
2720 gather_reserved_pts (GstWebRTCBin * webrtc)
2722 GstElement *element = GST_ELEMENT (webrtc);
2723 GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2726 GST_OBJECT_LOCK (webrtc);
2727 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
2728 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
2731 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2732 GstWebRTCRTPTransceiver *trans;
2734 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
2735 if (trans->codec_preferences) {
2739 n = gst_caps_get_size (trans->codec_preferences);
2740 for (j = 0; j < n; j++) {
2741 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
2742 if (gst_structure_get_int (s, "payload", &pt)) {
2743 GST_TRACE_OBJECT (trans, "have reserved pt %u from codec preferences",
2745 g_array_append_val (reserved_pts, pt);
2750 GST_OBJECT_UNLOCK (webrtc);
2752 return reserved_pts;
2756 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
2757 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
2758 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
2760 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2761 gchar *ufrag, *pwd, *sdp_mid;
2762 gboolean bundle_only = bundled_mids
2763 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2764 && gst_sdp_message_medias_len (msg) != bundle_idx;
2765 guint last_data_index = G_MAXUINT;
2767 /* add data channel support */
2768 if (webrtc->priv->data_channels->len == 0)
2772 last_data_index = _message_get_datachannel_index (last_offer);
2773 if (last_data_index < G_MAXUINT) {
2774 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
2775 /* XXX: is this always true when recycling transceivers?
2776 * i.e. do we always put the data channel in the same mline */
2777 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
2781 /* mandated by JSEP */
2782 gst_sdp_media_add_attribute (media, "setup", "actpass");
2784 /* FIXME: only needed when restarting ICE */
2785 if (last_offer && last_data_index < G_MAXUINT) {
2786 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
2787 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
2789 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2790 _generate_ice_credentials (&ufrag, &pwd);
2792 ufrag = g_strdup (bundle_ufrag);
2793 pwd = g_strdup (bundle_pwd);
2796 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2797 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2801 gst_sdp_media_set_media (media, "application");
2802 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
2803 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2804 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2805 gst_sdp_media_add_format (media, "webrtc-datachannel");
2807 if (bundle_idx != gst_sdp_message_medias_len (msg))
2808 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2810 if (last_offer && last_data_index < G_MAXUINT) {
2811 const GstSDPMedia *last_data_media;
2814 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
2815 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
2817 gst_sdp_media_add_attribute (media, "mid", mid);
2819 /* Make sure to avoid mid collisions */
2821 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2822 webrtc->priv->media_counter++);
2823 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
2826 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2827 g_hash_table_insert (all_mids, sdp_mid, NULL);
2834 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2837 g_string_append_printf (bundled_mids, " %s", mid);
2840 /* FIXME: negotiate this properly */
2841 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2843 _get_or_create_data_channel_transports (webrtc,
2844 bundled_mids ? 0 : webrtc->priv->transceivers->len);
2845 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
2850 /* TODO: use the options argument */
2851 static GstSDPMessage *
2852 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
2855 GstSDPMessage *ret = NULL;
2856 GString *bundled_mids = NULL;
2857 gchar *bundle_ufrag = NULL;
2858 gchar *bundle_pwd = NULL;
2859 GArray *reserved_pts = NULL;
2860 GHashTable *all_mids =
2861 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2863 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2864 GList *seen_transceivers = NULL;
2865 guint media_idx = 0;
2868 gst_sdp_message_new (&ret);
2870 gst_sdp_message_set_version (ret, "0");
2873 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
2875 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
2876 sess_id = g_strdup (origin->sess_id);
2878 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
2880 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
2884 gst_sdp_message_set_session_name (ret, "-");
2885 gst_sdp_message_add_time (ret, "0", "0", NULL);
2886 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
2888 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
2889 bundled_mids = g_string_new ("BUNDLE");
2890 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
2891 bundled_mids = g_string_new ("BUNDLE");
2894 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2895 GStrv last_bundle = NULL;
2896 guint bundle_media_index;
2898 reserved_pts = gather_reserved_pts (webrtc);
2899 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
2901 && last_bundle && last_bundle && last_bundle[0]
2903 && last_bundle && last_bundle[0]
2905 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
2907 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
2909 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
2911 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2914 g_strfreev (last_bundle);
2917 /* FIXME: recycle transceivers */
2919 /* Fill up the renegotiated streams first */
2921 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
2922 GstWebRTCRTPTransceiver *trans = NULL;
2923 const GstSDPMedia *last_media;
2925 last_media = gst_sdp_message_get_media (last_offer, i);
2927 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
2928 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
2929 const gchar *last_mid;
2931 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
2933 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2934 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
2936 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
2940 g_assert (!g_list_find (seen_transceivers, trans));
2942 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
2943 GST_PTR_FORMAT " with mid %s into media index %u", trans,
2944 trans->mid, media_idx);
2946 /* FIXME: deal with format changes */
2947 gst_sdp_media_copy (last_media, &media);
2948 _media_replace_direction (media, trans->direction);
2950 mid = gst_sdp_media_get_attribute_val (media, "mid");
2953 if (g_hash_table_contains (all_mids, mid)) {
2954 gst_sdp_media_free (media);
2955 g_set_error (error, GST_WEBRTC_BIN_ERROR,
2956 GST_WEBRTC_BIN_ERROR_FAILED,
2957 "Duplicate mid %s when creating offer", mid);
2961 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
2964 g_string_append_printf (bundled_mids, " %s", mid);
2966 gst_sdp_message_add_media (ret, media);
2969 gst_sdp_media_free (media);
2970 seen_transceivers = g_list_prepend (seen_transceivers, trans);
2974 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
2975 "application") == 0) {
2976 GstSDPMedia media = { 0, };
2977 gst_sdp_media_init (&media);
2978 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
2979 bundle_ufrag, bundle_pwd, all_mids)) {
2980 gst_sdp_message_add_media (ret, &media);
2983 gst_sdp_media_uninit (&media);
2989 /* First, go over all transceivers and gather existing mids */
2990 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2991 GstWebRTCRTPTransceiver *trans;
2993 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
2995 if (g_list_find (seen_transceivers, trans))
2999 if (g_hash_table_contains (all_mids, trans->mid)) {
3000 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
3001 "Duplicate mid %s when creating offer", trans->mid);
3005 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3009 /* add any extra streams */
3010 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3011 GstWebRTCRTPTransceiver *trans;
3012 GstSDPMedia media = { 0, };
3014 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3016 /* don't add transceivers twice */
3017 if (g_list_find (seen_transceivers, trans))
3020 /* don't add stopped transceivers */
3024 gst_sdp_media_init (&media);
3026 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3027 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3030 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3031 "index %u", trans, media_idx);
3033 if (sdp_media_from_transceiver (webrtc, &media, trans,
3034 GST_WEBRTC_SDP_TYPE_OFFER, media_idx, bundled_mids, 0, bundle_ufrag,
3035 bundle_pwd, reserved_pts, all_mids)) {
3036 gst_sdp_message_add_media (ret, &media);
3039 gst_sdp_media_uninit (&media);
3042 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3043 g_array_free (reserved_pts, TRUE);
3044 reserved_pts = NULL;
3046 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3049 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3050 g_array_free (reserved_pts, TRUE);
3051 reserved_pts = NULL;
3054 /* add a data channel if exists and not renegotiated */
3055 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3056 GstSDPMedia media = { 0, };
3057 gst_sdp_media_init (&media);
3058 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3059 bundle_ufrag, bundle_pwd, all_mids)) {
3060 gst_sdp_message_add_media (ret, &media);
3063 gst_sdp_media_uninit (&media);
3067 g_assert (media_idx == gst_sdp_message_medias_len (ret));
3070 gchar *mids = g_string_free (bundled_mids, FALSE);
3072 gst_sdp_message_add_attribute (ret, "group", mids);
3074 bundled_mids = NULL;
3077 /* FIXME: pre-emptively setup receiving elements when needed */
3079 if (webrtc->priv->last_generated_answer)
3080 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3081 webrtc->priv->last_generated_answer = NULL;
3082 if (webrtc->priv->last_generated_offer)
3083 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3085 GstSDPMessage *copy;
3086 gst_sdp_message_copy (ret, ©);
3087 webrtc->priv->last_generated_offer =
3088 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3093 g_array_free (reserved_pts, TRUE);
3095 g_hash_table_unref (all_mids);
3097 g_list_free (seen_transceivers);
3100 g_free (bundle_ufrag);
3103 g_free (bundle_pwd);
3106 g_string_free (bundled_mids, TRUE);
3111 gst_sdp_message_uninit (ret);
3117 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3118 gint * rtx_target_pt)
3122 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3125 for (i = 0; i < gst_caps_get_size (caps); i++) {
3126 const GstStructure *s = gst_caps_get_structure (caps, i);
3128 if (gst_structure_has_name (s, "application/x-rtp")) {
3129 const gchar *encoding_name =
3130 gst_structure_get_string (s, "encoding-name");
3134 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3135 gst_structure_get_int (s, "payload", &pt)) {
3136 if (!g_strcmp0 (encoding_name, "RED")) {
3139 str = g_strdup_printf ("%u", pt);
3140 gst_sdp_media_add_format (media, str);
3142 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3143 *rtx_target_pt = pt;
3144 gst_sdp_media_add_attribute (media, "rtpmap", str);
3146 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3149 str = g_strdup_printf ("%u", pt);
3150 gst_sdp_media_add_format (media, str);
3152 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3153 gst_sdp_media_add_attribute (media, "rtpmap", str);
3162 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3163 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3166 const GstStructure *s;
3168 if (trans->local_rtx_ssrc_map)
3169 gst_structure_free (trans->local_rtx_ssrc_map);
3171 trans->local_rtx_ssrc_map =
3172 gst_structure_new_empty ("application/x-rtp-ssrc-map");
3174 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
3175 s = gst_caps_get_structure (offer_caps, i);
3177 if (gst_structure_has_name (s, "application/x-rtp")) {
3178 const gchar *encoding_name =
3179 gst_structure_get_string (s, "encoding-name");
3180 const gchar *apt_str = gst_structure_get_string (s, "apt");
3188 apt = atoi (apt_str);
3190 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3191 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
3192 if (!g_strcmp0 (encoding_name, "RTX")) {
3195 str = g_strdup_printf ("%u", pt);
3196 gst_sdp_media_add_format (media, str);
3198 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
3199 gst_sdp_media_add_attribute (media, "rtpmap", str);
3202 str = g_strdup_printf ("%d apt=%d", pt, apt);
3203 gst_sdp_media_add_attribute (media, "fmtp", str);
3206 str = g_strdup_printf ("%u", target_ssrc);
3207 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
3208 g_random_int (), NULL);
3215 static GstWebRTCKind
3216 _kind_from_caps (const GstCaps * caps)
3221 if (gst_caps_get_size (caps) == 0)
3222 return GST_WEBRTC_KIND_UNKNOWN;
3224 s = gst_caps_get_structure (caps, 0);
3226 media = gst_structure_get_string (s, "media");
3228 return GST_WEBRTC_KIND_UNKNOWN;
3230 if (!g_strcmp0 (media, "audio"))
3231 return GST_WEBRTC_KIND_AUDIO;
3233 if (!g_strcmp0 (media, "video"))
3234 return GST_WEBRTC_KIND_VIDEO;
3236 return GST_WEBRTC_KIND_UNKNOWN;
3240 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
3241 const GstCaps * caps)
3243 GstWebRTCKind kind = _kind_from_caps (caps);
3245 if (trans->kind == kind)
3248 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
3257 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
3258 guint * target_ssrc)
3260 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
3262 gst_structure_get_int (s, "payload", target_pt);
3263 gst_structure_get_uint (s, "ssrc", target_ssrc);
3266 /* TODO: use the options argument */
3267 static GstSDPMessage *
3268 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3271 GstSDPMessage *ret = NULL;
3272 const GstWebRTCSessionDescription *pending_remote =
3273 webrtc->pending_remote_description;
3275 GStrv bundled = NULL;
3276 guint bundle_idx = 0;
3277 GString *bundled_mids = NULL;
3278 gchar *bundle_ufrag = NULL;
3279 gchar *bundle_pwd = NULL;
3280 GList *seen_transceivers = NULL;
3281 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
3283 if (!webrtc->pending_remote_description) {
3284 g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
3285 GST_WEBRTC_BIN_ERROR_INVALID_STATE,
3286 "Asked to create an answer without a remote description");
3290 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
3294 GStrv last_bundle = NULL;
3295 guint bundle_media_index;
3297 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
3298 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
3299 "Bundle tag is %s but no media found matching", bundled[0]);
3303 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3304 bundled_mids = g_string_new ("BUNDLE");
3307 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
3308 && last_bundle && last_bundle[0]
3309 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
3311 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
3313 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
3315 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3318 g_strfreev (last_bundle);
3321 gst_sdp_message_new (&ret);
3323 gst_sdp_message_set_version (ret, "0");
3325 const GstSDPOrigin *offer_origin =
3326 gst_sdp_message_get_origin (pending_remote->sdp);
3327 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
3328 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
3330 gst_sdp_message_set_session_name (ret, "-");
3332 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
3333 const GstSDPAttribute *attr =
3334 gst_sdp_message_get_attribute (pending_remote->sdp, i);
3336 if (g_strcmp0 (attr->key, "ice-options") == 0) {
3337 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
3341 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
3342 GstSDPMedia *media = NULL;
3343 GstSDPMedia *offer_media;
3344 GstWebRTCDTLSSetup offer_setup, answer_setup;
3346 gboolean bundle_only;
3350 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
3351 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
3353 gst_sdp_media_new (&media);
3354 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3355 gst_sdp_media_set_port_info (media, 0, 0);
3357 gst_sdp_media_set_port_info (media, 9, 0);
3358 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3363 /* FIXME: deal with ICE restarts */
3364 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
3365 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
3366 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
3369 _generate_ice_credentials (&ufrag, &pwd);
3371 ufrag = g_strdup (bundle_ufrag);
3372 pwd = g_strdup (bundle_pwd);
3375 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3376 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3381 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
3382 const GstSDPAttribute *attr =
3383 gst_sdp_media_get_attribute (offer_media, j);
3385 if (g_strcmp0 (attr->key, "mid") == 0
3386 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
3387 gst_sdp_media_add_attribute (media, attr->key, attr->value);
3388 /* FIXME: handle anything we want to keep */
3392 mid = gst_sdp_media_get_attribute_val (media, "mid");
3393 /* XXX: not strictly required but a lot of functionality requires a mid */
3396 /* set the a=setup: attribute */
3397 offer_setup = _get_dtls_setup_from_media (offer_media);
3398 answer_setup = _intersect_dtls_setup (offer_setup);
3399 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
3400 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
3401 "transceiver direction");
3404 _media_replace_setup (media, answer_setup);
3406 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
3409 if (gst_sdp_media_formats_len (offer_media) != 1) {
3410 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
3411 "for webrtc-datachannel");
3414 sctp_port = _get_sctp_port_from_media (offer_media);
3415 if (sctp_port == -1) {
3416 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
3420 /* XXX: older browsers will produce a different SDP format for data
3421 * channel that is currently not parsed correctly */
3422 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3424 gst_sdp_media_set_media (media, "application");
3425 gst_sdp_media_set_port_info (media, 9, 0);
3426 gst_sdp_media_add_format (media, "webrtc-datachannel");
3428 /* FIXME: negotiate this properly on renegotiation */
3429 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3431 _get_or_create_data_channel_transports (webrtc,
3432 bundled_mids ? bundle_idx : i);
3436 g_string_append_printf (bundled_mids, " %s", mid);
3439 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
3441 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
3442 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
3443 GstCaps *offer_caps, *answer_caps = NULL;
3444 GstWebRTCRTPTransceiver *rtp_trans = NULL;
3445 WebRTCTransceiver *trans = NULL;
3446 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
3447 gint target_pt = -1;
3448 gint original_target_pt = -1;
3449 guint target_ssrc = 0;
3451 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3452 offer_caps = _rtp_caps_from_media (offer_media);
3454 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
3456 _find_transceiver (webrtc, mid,
3457 (FindTransceiverFunc) match_for_mid))) {
3458 const GstSDPMedia *last_media =
3459 gst_sdp_message_get_media (last_answer, i);
3460 const gchar *last_mid =
3461 gst_sdp_media_get_attribute_val (last_media, "mid");
3463 /* FIXME: assumes no shenanigans with recycling transceivers */
3464 g_assert (g_strcmp0 (mid, last_mid) == 0);
3467 && (rtp_trans->direction ==
3468 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
3469 || rtp_trans->direction ==
3470 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY))
3472 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
3474 && (rtp_trans->direction ==
3475 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
3476 || rtp_trans->direction ==
3477 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY))
3479 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SRC, i);
3481 answer_caps = _rtp_caps_from_media (last_media);
3483 /* XXX: In theory we're meant to use the sendrecv formats for the
3484 * inactive direction however we don't know what that may be and would
3485 * require asking outside what it expects to possibly send later */
3487 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
3488 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
3489 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
3491 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3492 GstCaps *trans_caps;
3494 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3496 if (g_list_find (seen_transceivers, rtp_trans)) {
3497 /* Don't double allocate a transceiver to multiple mlines */
3503 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
3505 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
3506 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
3508 /* FIXME: technically this is a little overreaching as some fields we
3509 * we can deal with not having and/or we may have unrecognized fields
3510 * that we cannot actually support */
3512 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
3513 if (answer_caps && !gst_caps_is_empty (answer_caps)) {
3514 GST_LOG_OBJECT (webrtc,
3515 "found compatible transceiver %" GST_PTR_FORMAT
3516 " for offer media %u", rtp_trans, i);
3518 gst_caps_unref (trans_caps);
3522 gst_caps_unref (answer_caps);
3526 gst_caps_unref (trans_caps);
3536 answer_dir = rtp_trans->direction;
3537 g_assert (answer_caps != NULL);
3539 /* if no transceiver, then we only receive that stream and respond with
3540 * the exact same caps */
3541 /* FIXME: how to validate that subsequent elements can actually receive
3542 * this payload/format */
3543 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
3544 answer_caps = gst_caps_ref (offer_caps);
3547 if (gst_caps_is_empty (answer_caps)) {
3548 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
3550 gst_object_unref (rtp_trans);
3551 gst_caps_unref (answer_caps);
3555 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
3558 trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
3559 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
3561 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
3562 " for mline %u", trans, i);
3564 trans = WEBRTC_TRANSCEIVER (rtp_trans);
3566 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps))
3567 GST_WARNING_OBJECT (webrtc,
3568 "Trying to change transceiver %d kind from %d to %d",
3569 rtp_trans->mline, rtp_trans->kind, _kind_from_caps (answer_caps));
3571 if (!trans->do_nack) {
3572 answer_caps = gst_caps_make_writable (answer_caps);
3573 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
3574 GstStructure *s = gst_caps_get_structure (answer_caps, k);
3575 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
3579 gst_sdp_media_set_media_from_caps (answer_caps, media);
3581 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
3584 original_target_pt = target_pt;
3586 _media_add_fec (media, trans, offer_caps, &target_pt);
3587 if (trans->do_nack) {
3588 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
3589 if (target_pt != original_target_pt)
3590 _media_add_rtx (media, trans, offer_caps, original_target_pt,
3594 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
3595 _media_add_ssrcs (media, answer_caps, webrtc,
3596 WEBRTC_TRANSCEIVER (rtp_trans));
3598 gst_caps_unref (answer_caps);
3601 /* set the new media direction */
3602 offer_dir = _get_direction_from_media (offer_media);
3603 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
3604 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
3605 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
3606 "transceiver direction");
3609 _media_replace_direction (media, answer_dir);
3611 if (!trans->stream) {
3612 TransportStream *item;
3615 _get_or_create_transport_stream (webrtc,
3616 bundled_mids ? bundle_idx : i, FALSE);
3617 webrtc_transceiver_set_transport (trans, item);
3621 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3624 g_string_append_printf (bundled_mids, " %s", mid);
3627 /* set the a=fingerprint: for this transport */
3628 _add_fingerprint_to_media (trans->stream->transport, media);
3630 gst_caps_unref (offer_caps);
3632 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
3638 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
3639 gst_sdp_media_free (media);
3640 gst_sdp_media_copy (offer_media, &media);
3641 gst_sdp_media_set_port_info (media, 0, 0);
3643 gst_sdp_message_add_media (ret, media);
3644 gst_sdp_media_free (media);
3648 gchar *mids = g_string_free (bundled_mids, FALSE);
3650 gst_sdp_message_add_attribute (ret, "group", mids);
3655 g_free (bundle_ufrag);
3658 g_free (bundle_pwd);
3660 /* FIXME: can we add not matched transceivers? */
3662 /* XXX: only true for the initial offerer */
3663 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
3666 g_strfreev (bundled);
3668 g_list_free (seen_transceivers);
3670 if (webrtc->priv->last_generated_offer)
3671 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3672 webrtc->priv->last_generated_offer = NULL;
3673 if (webrtc->priv->last_generated_answer)
3674 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3676 GstSDPMessage *copy;
3677 gst_sdp_message_copy (ret, ©);
3678 webrtc->priv->last_generated_answer =
3679 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
3687 GstStructure *options;
3688 GstPromise *promise;
3689 GstWebRTCSDPType type;
3693 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
3695 GstWebRTCSessionDescription *desc = NULL;
3696 GstSDPMessage *sdp = NULL;
3697 GstStructure *s = NULL;
3698 GError *error = NULL;
3700 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
3701 gst_webrtc_sdp_type_to_string (data->type), data->options);
3703 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
3704 sdp = _create_offer_task (webrtc, data->options, &error);
3705 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
3706 sdp = _create_answer_task (webrtc, data->options, &error);
3708 g_assert_not_reached ();
3713 desc = gst_webrtc_session_description_new (data->type, sdp);
3714 s = gst_structure_new ("application/x-gst-promise",
3715 gst_webrtc_sdp_type_to_string (data->type),
3716 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
3718 g_warn_if_fail (error != NULL);
3719 GST_WARNING_OBJECT (webrtc, "returning error: %s",
3720 error ? error->message : "Unknown");
3721 s = gst_structure_new ("application/x-gstwebrtcbin-error",
3722 "error", G_TYPE_ERROR, error, NULL);
3723 g_clear_error (&error);
3728 gst_promise_reply (data->promise, s);
3732 gst_webrtc_session_description_free (desc);
3736 _free_create_sdp_data (struct create_sdp *data)
3739 gst_structure_free (data->options);
3740 gst_promise_unref (data->promise);
3745 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
3746 const GstStructure * options, GstPromise * promise)
3748 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3751 data->options = gst_structure_copy (options);
3752 data->promise = gst_promise_ref (promise);
3753 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
3755 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3756 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
3758 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
3759 "Could not create offer. webrtcbin is closed");
3761 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
3762 "error", G_TYPE_ERROR, error, NULL);
3764 gst_promise_reply (promise, s);
3766 g_clear_error (&error);
3771 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
3772 const GstStructure * options, GstPromise * promise)
3774 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3777 data->options = gst_structure_copy (options);
3778 data->promise = gst_promise_ref (promise);
3779 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
3781 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3782 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
3784 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
3785 "Could not create answer. webrtcbin is closed.");
3787 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
3788 "error", G_TYPE_ERROR, error, NULL);
3790 gst_promise_reply (promise, s);
3792 g_clear_error (&error);
3796 static GstWebRTCBinPad *
3797 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
3800 GstWebRTCBinPad *pad;
3804 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
3806 pad = gst_webrtc_bin_pad_new (pad_name, direction);
3808 pad->mlineindex = media_idx;
3813 static GstWebRTCRTPTransceiver *
3814 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
3815 const GstSDPMessage * sdp, guint media_idx)
3817 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3818 GstWebRTCRTPTransceiver *ret = NULL;
3821 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3822 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3824 if (g_strcmp0 (attr->key, "mid") == 0) {
3826 _find_transceiver (webrtc, attr->value,
3827 (FindTransceiverFunc) match_for_mid)))
3832 ret = _find_transceiver (webrtc, &media_idx,
3833 (FindTransceiverFunc) transceiver_match_for_mline);
3836 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
3840 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
3842 _insert_netsim_element_between (GstWebRTCBin * webrtc, GstElement * srcbin,
3843 const gchar * srcpadname, GstElement * sinkbin, const gchar * sinkpadname)
3845 GstElement *netsim = gst_element_factory_make ("netsim", NULL);
3846 gst_bin_add (GST_BIN (webrtc), netsim);
3847 g_object_set (netsim, "drop-probability", webrtc->priv->drop_probability_sender, NULL);
3848 gst_element_sync_state_with_parent (netsim);
3850 if (!gst_element_link_pads (srcbin, srcpadname, netsim, "sink"))
3851 g_warn_if_reached ();
3853 if (!gst_element_link_pads (netsim, "src", sinkbin, sinkpadname))
3854 g_warn_if_reached ();
3859 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
3864 * ,-------------------------webrtcbin-------------------------,
3866 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3867 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3869 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3870 * ; sink_%u ; ; '---------------------' ;
3871 * o----------o send_rtp_sink_%u ; ;
3872 * ; '--------------------' ;
3873 * '--------------------- -------------------------------------'
3878 * ,--------------------------------webrtcbin--------------------------------,
3880 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3881 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3883 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3884 * ; sink_%u ,---funnel---, ; ; '---------------------' ;
3885 * o---------o sink_%u ; ; ; ;
3886 * ; sink_%u ; src o-o send_rtp_sink_%u ; ;
3887 * o---------o sink_%u ; ; ; ;
3888 * ; '------------' '--------------------' ;
3889 * '-------------------------------------------------------------------------'
3891 GstPadTemplate *rtp_templ;
3894 WebRTCTransceiver *trans;
3896 g_return_val_if_fail (pad->trans != NULL, NULL);
3898 GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
3900 trans = WEBRTC_TRANSCEIVER (pad->trans);
3902 g_assert (trans->stream);
3904 if (!webrtc->rtpfunnel) {
3906 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
3907 "send_rtp_sink_%u");
3908 g_assert (rtp_templ);
3910 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
3912 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
3914 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
3915 gst_object_unref (rtp_sink);
3917 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
3918 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
3919 if (webrtc->priv->netsim) {
3920 _insert_netsim_element_between (webrtc, GST_ELEMENT (webrtc->rtpbin), pad_name,
3921 GST_ELEMENT (trans->stream->send_bin), "rtp_sink");
3924 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3925 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
3926 g_warn_if_reached ();
3927 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
3932 gchar *pad_name = g_strdup_printf ("sink_%u", pad->mlineindex);
3933 GstPad *funnel_sinkpad =
3934 gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
3936 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
3939 gst_object_unref (funnel_sinkpad);
3942 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
3944 return GST_PAD (pad);
3947 /* output pads are receiving elements */
3949 _connect_output_stream (GstWebRTCBin * webrtc,
3950 TransportStream * stream, guint session_id)
3953 * ,------------------------webrtcbin------------------------,
3954 * ; ,---------rtpbin---------, ;
3955 * ; ,-transport_receive_%u--, ; ; ;
3956 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
3958 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
3959 * ; '-----------------------' ; ; ; src_%u
3960 * ; ; recv_rtp_src_%u_%u_%u o--o
3961 * ; '------------------------' ;
3962 * '---------------------------------------------------------'
3966 if (stream->output_connected) {
3967 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
3968 "connected to rtpbin. Not connecting", stream);
3972 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
3973 session_id, stream);
3975 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
3976 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
3977 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
3978 g_warn_if_reached ();
3981 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
3983 /* The webrtcbin src_%u output pads will be created when rtpbin receives
3984 * data on that stream in on_rtpbin_pad_added() */
3986 stream->output_connected = TRUE;
3996 _clear_ice_candidate_item (IceCandidateItem * item)
3998 g_free (item->candidate);
4002 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
4003 gboolean drop_invalid)
4005 GstWebRTCICEStream *stream;
4007 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
4008 if (stream == NULL) {
4010 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
4013 IceCandidateItem new;
4014 new.mlineindex = item->mlineindex;
4015 new.candidate = g_strdup (item->candidate);
4016 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
4019 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
4020 ICE_UNLOCK (webrtc);
4025 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4026 item->mlineindex, item->candidate);
4028 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
4032 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
4033 const GstSDPMedia * media)
4036 GstWebRTCICEStream *stream = NULL;
4038 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
4039 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
4040 if (g_strcmp0 (attr->key, "candidate") == 0) {
4044 stream = _find_ice_stream_for_session (webrtc, mlineindex);
4045 if (stream == NULL) {
4046 GST_WARNING_OBJECT (webrtc,
4047 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
4051 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
4052 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4053 mlineindex, candidate);
4054 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
4061 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
4062 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
4064 GstSDPMedia *media = NULL;
4066 if (mline_index < sdp->medias->len) {
4067 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
4070 if (media == NULL) {
4071 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
4075 // Add the candidate as an attribute, first stripping off the existing
4076 // candidate: key from the string description
4077 if (strlen (candidate) < 10) {
4078 GST_WARNING_OBJECT (webrtc,
4079 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
4083 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
4087 _filter_sdp_fields (GQuark field_id, const GValue * value,
4088 GstStructure * new_structure)
4090 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
4091 gst_structure_id_set_value (new_structure, field_id, value);
4097 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
4102 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4103 GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4105 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4108 for (i = 0; i < rtx_count; i++) {
4109 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4110 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4111 const gchar *apt = gst_structure_get_string (s, "apt");
4113 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4114 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4117 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4118 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4119 stream->rtxsend, pt_map);
4121 if (stream->rtxreceive)
4122 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4123 if (stream->rtxsend)
4124 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4126 gst_structure_free (pt_map);
4134 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
4135 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
4139 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4142 proto = gst_sdp_media_get_proto (media);
4143 if (proto != NULL) {
4144 /* Parse global SDP attributes once */
4145 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
4146 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
4147 gst_sdp_message_attributes_to_caps (sdp, global_caps);
4148 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
4149 gst_sdp_media_attributes_to_caps (media, global_caps);
4151 len = gst_sdp_media_formats_len (media);
4152 for (i = 0; i < len; i++) {
4153 GstCaps *caps, *outcaps;
4159 pt = atoi (gst_sdp_media_get_format (media, i));
4161 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
4164 caps = gst_sdp_media_get_caps_from_media (media, pt);
4166 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
4170 /* Merge in global caps */
4171 /* Intersect will merge in missing fields to the current caps */
4172 outcaps = gst_caps_intersect (caps, global_caps);
4173 gst_caps_unref (caps);
4175 s = gst_caps_get_structure (outcaps, 0);
4176 gst_structure_set_name (s, "application/x-rtp");
4177 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
4178 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
4180 item.caps = gst_caps_new_empty ();
4182 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
4183 GstStructure *s = gst_caps_get_structure (outcaps, j);
4184 GstStructure *filtered =
4185 gst_structure_new_empty (gst_structure_get_name (s));
4187 gst_structure_foreach (s,
4188 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
4189 gst_caps_append_structure (item.caps, filtered);
4193 gst_caps_unref (outcaps);
4195 g_array_append_val (stream->ptmap, item);
4198 gst_caps_unref (global_caps);
4203 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
4204 const GstSDPMessage * sdp, guint media_idx,
4205 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
4206 GStrv bundled, guint bundle_idx, GError ** error)
4208 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4209 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
4210 GstWebRTCRTPTransceiverDirection new_dir;
4211 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4212 GstWebRTCDTLSSetup new_setup;
4213 gboolean new_rtcp_rsize;
4214 ReceiveState receive_state = RECEIVE_STATE_UNSET;
4217 rtp_trans->mline = media_idx;
4219 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
4220 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
4221 GST_FIXME_OBJECT (webrtc,
4222 "Updating video transceiver to audio, which isn't fully supported.");
4223 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
4226 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
4227 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
4228 GST_FIXME_OBJECT (webrtc,
4229 "Updating audio transceiver to video, which isn't fully supported.");
4230 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
4233 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4234 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4236 if (g_strcmp0 (attr->key, "mid") == 0) {
4237 g_free (rtp_trans->mid);
4238 rtp_trans->mid = g_strdup (attr->value);
4243 const GstSDPMedia *local_media, *remote_media;
4244 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
4245 GstWebRTCDTLSSetup local_setup, remote_setup;
4248 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4251 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4254 local_setup = _get_dtls_setup_from_media (local_media);
4255 remote_setup = _get_dtls_setup_from_media (remote_media);
4256 new_setup = _get_final_setup (local_setup, remote_setup);
4257 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4258 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4259 "Cannot intersect direction attributes for media %u", media_idx);
4263 local_dir = _get_direction_from_media (local_media);
4264 remote_dir = _get_direction_from_media (remote_media);
4265 new_dir = _get_final_direction (local_dir, remote_dir);
4266 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4267 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4268 "Cannot intersect dtls setup attributes for media %u", media_idx);
4272 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
4273 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
4274 && prev_dir != new_dir) {
4275 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4276 GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
4277 "transceiver direction changes are not implemented. Media %u",
4282 if (!bundled || bundle_idx == media_idx) {
4283 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
4284 && _media_has_attribute_key (remote_media, "rtcp-rsize");
4288 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
4289 media_idx, &session);
4291 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
4292 g_object_unref (session);
4298 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4300 /* Not a bundled stream means this entire transport is inactive,
4301 * so set the receive state to BLOCK below */
4302 stream->active = FALSE;
4303 receive_state = RECEIVE_STATE_BLOCK;
4306 /* If this transceiver is active for sending or receiving,
4307 * we still need receive at least RTCP, so need to unblock
4308 * the receive bin below. */
4309 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
4310 receive_state = RECEIVE_STATE_PASS;
4311 stream->active = TRUE;
4314 if (new_dir != prev_dir) {
4315 gchar *prev_dir_s, *new_dir_s;
4318 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4321 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4324 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
4325 " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
4327 g_free (prev_dir_s);
4332 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4333 GstWebRTCBinPad *pad;
4335 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
4337 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4339 GstPad *peer = gst_pad_get_peer (target);
4341 gst_pad_send_event (peer, gst_event_new_eos ());
4342 gst_object_unref (peer);
4344 gst_object_unref (target);
4346 gst_object_unref (pad);
4349 /* XXX: send eos event up the sink pad as well? */
4352 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
4353 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4354 GstWebRTCBinPad *pad =
4355 _find_pad_for_mline (webrtc, GST_PAD_SINK, media_idx);
4357 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
4358 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4359 g_assert (pad->trans == rtp_trans);
4360 g_assert (pad->mlineindex == media_idx);
4361 gst_object_unref (pad);
4363 GST_DEBUG_OBJECT (webrtc,
4364 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
4365 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, media_idx);
4366 pad->trans = gst_object_ref (rtp_trans);
4367 _connect_input_stream (webrtc, pad);
4368 _add_pad (webrtc, pad);
4371 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
4372 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4373 GstWebRTCBinPad *pad =
4374 _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
4376 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
4377 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4378 g_assert (pad->trans == rtp_trans);
4379 g_assert (pad->mlineindex == media_idx);
4380 gst_object_unref (pad);
4382 GST_DEBUG_OBJECT (webrtc,
4383 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
4384 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
4385 pad->trans = gst_object_ref (rtp_trans);
4387 if (!trans->stream) {
4388 TransportStream *item;
4391 _get_or_create_transport_stream (webrtc,
4392 bundled ? bundle_idx : media_idx, FALSE);
4393 webrtc_transceiver_set_transport (trans, item);
4396 _connect_output_stream (webrtc, trans->stream,
4397 bundled ? bundle_idx : media_idx);
4398 /* delay adding the pad until rtpbin creates the recv output pad
4399 * to ghost to so queries/events travel through the pipeline correctly
4400 * as soon as the pad is added */
4401 _add_pad_to_list (webrtc, pad);
4406 rtp_trans->mline = media_idx;
4407 rtp_trans->current_direction = new_dir;
4410 if (!bundled || bundle_idx == media_idx) {
4411 if (stream->rtxsend || stream->rtxreceive) {
4412 _set_rtx_ptmap_from_stream (webrtc, stream);
4415 g_object_set (stream, "dtls-client",
4416 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4419 /* Must be after setting the "dtls-client" so that data is not pushed into
4420 * the dtlssrtp elements before the ssl direction has been set which will
4421 * throw SSL errors */
4422 if (receive_state != RECEIVE_STATE_UNSET)
4423 transport_receive_bin_set_receive_state (stream->receive_bin,
4427 /* must be called with the pc lock held */
4429 _generate_data_channel_id (GstWebRTCBin * webrtc)
4432 gint new_id = -1, max_channels = 0;
4434 if (webrtc->priv->sctp_transport) {
4435 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
4438 if (max_channels <= 0) {
4439 max_channels = 65534;
4442 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
4445 /* TODO: a better search algorithm */
4447 WebRTCDataChannel *channel;
4451 if (new_id < 0 || new_id >= max_channels) {
4452 /* exhausted id space */
4453 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
4454 "data channel id (max %i)", max_channels);
4458 /* client must generate even ids, server must generate odd ids */
4459 if (new_id % 2 == ! !is_client)
4462 channel = _find_data_channel_for_id (webrtc, new_id);
4471 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
4472 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
4475 const GstSDPMedia *local_media, *remote_media;
4476 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
4477 TransportReceiveBin *receive;
4478 int local_port, remote_port;
4479 guint64 local_max_size, remote_max_size, max_size;
4483 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4486 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4489 local_setup = _get_dtls_setup_from_media (local_media);
4490 remote_setup = _get_dtls_setup_from_media (remote_media);
4491 new_setup = _get_final_setup (local_setup, remote_setup);
4492 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4493 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4494 "Cannot intersect dtls setup for media %u", media_idx);
4498 /* data channel is always rtcp-muxed to avoid generating ICE candidates
4500 g_object_set (stream, "dtls-client",
4501 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4503 local_port = _get_sctp_port_from_media (local_media);
4504 remote_port = _get_sctp_port_from_media (local_media);
4505 if (local_port == -1 || remote_port == -1) {
4506 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4507 "Could not find sctp port for media %u (local %i, remote %i)",
4508 media_idx, local_port, remote_port);
4512 if (0 == (local_max_size =
4513 _get_sctp_max_message_size_from_media (local_media)))
4514 local_max_size = G_MAXUINT64;
4515 if (0 == (remote_max_size =
4516 _get_sctp_max_message_size_from_media (remote_media)))
4517 remote_max_size = G_MAXUINT64;
4518 max_size = MIN (local_max_size, remote_max_size);
4520 webrtc->priv->sctp_transport->max_message_size = max_size;
4523 guint orig_local_port, orig_remote_port;
4525 /* XXX: sctpassociation warns if we are in the wrong state */
4526 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
4527 &orig_local_port, NULL);
4529 if (orig_local_port != local_port)
4530 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
4533 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
4534 &orig_remote_port, NULL);
4535 if (orig_remote_port != remote_port)
4536 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
4540 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
4541 WebRTCDataChannel *channel;
4543 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
4545 if (channel->parent.id == -1)
4546 channel->parent.id = _generate_data_channel_id (webrtc);
4547 if (channel->parent.id == -1)
4548 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
4549 ("%s", "Failed to generate an identifier for a data channel"), NULL);
4551 if (webrtc->priv->sctp_transport->association_established
4552 && !channel->parent.negotiated && !channel->opened) {
4553 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
4554 webrtc_data_channel_start_negotiation (channel);
4558 stream->active = TRUE;
4560 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
4561 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
4565 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
4570 if (p1->mline != -1)
4579 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
4582 GstPad *queue_srcpad;
4584 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
4589 if (webrtc->rtpfunnel)
4592 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
4593 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
4594 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
4596 queue = gst_element_factory_make ("queue", NULL);
4597 gst_bin_add (GST_BIN (webrtc), queue);
4598 gst_element_sync_state_with_parent (queue);
4600 gst_element_link (webrtc->rtpfunnel, queue);
4602 queue_srcpad = gst_element_get_static_pad (queue, "src");
4604 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
4605 rtp_sink = gst_element_get_request_pad (webrtc->rtpbin, pad_name);
4607 gst_pad_link (queue_srcpad, rtp_sink);
4608 gst_object_unref (queue_srcpad);
4609 gst_object_unref (rtp_sink);
4611 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
4612 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
4613 if (webrtc->priv->netsim) {
4614 _insert_netsim_element_between (webrtc, GST_ELEMENT (webrtc->rtpbin), pad_name,
4615 GST_ELEMENT (stream->send_bin), "rtp_sink");
4618 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4619 GST_ELEMENT (stream->send_bin), "rtp_sink"))
4620 g_warn_if_reached ();
4621 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
4631 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
4632 GstWebRTCSessionDescription * sdp, GError ** error)
4635 gboolean ret = FALSE;
4636 GStrv bundled = NULL;
4637 guint bundle_idx = 0;
4638 TransportStream *bundle_stream = NULL;
4640 /* FIXME: With some peers, it's possible we could have
4641 * multiple bundles to deal with, although I've never seen one yet */
4642 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
4643 if (!_parse_bundle (sdp->sdp, &bundled, error))
4648 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
4649 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4650 "Bundle tag is %s but no media found matching", bundled[0]);
4654 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
4655 _message_media_is_datachannel (sdp->sdp, bundle_idx));
4656 /* Mark the bundle stream as inactive to start. It will be set to TRUE
4657 * by any bundled mline that is active, and at the end we set the
4658 * receivebin to BLOCK if all mlines were inactive. */
4659 bundle_stream->active = FALSE;
4661 g_array_set_size (bundle_stream->ptmap, 0);
4662 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4663 /* When bundling, we need to do this up front, or else RTX
4664 * parameters aren't set up properly for the bundled streams */
4665 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
4668 _connect_rtpfunnel (webrtc, bundle_idx);
4671 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4672 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
4673 TransportStream *stream;
4674 GstWebRTCRTPTransceiver *trans;
4675 guint transport_idx;
4677 /* skip rejected media */
4678 if (gst_sdp_media_get_port (media) == 0)
4682 transport_idx = bundle_idx;
4686 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
4688 stream = _get_or_create_transport_stream (webrtc, transport_idx,
4689 _message_media_is_datachannel (sdp->sdp, transport_idx));
4691 /* When bundling, these were all set up above, but when not
4692 * bundling we need to do it now */
4693 g_array_set_size (stream->ptmap, 0);
4694 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
4698 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
4700 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
4701 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4702 "State mismatch. Could not find local transceiver by mline %u", i);
4705 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
4706 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
4707 /* No existing transceiver, find an unused one */
4709 trans = _find_transceiver (webrtc, NULL,
4710 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
4713 /* Still no transceiver? Create one */
4714 /* XXX: default to the advertised direction in the sdp for new
4715 * transceivers. The spec doesn't actually say what happens here, only
4716 * that calls to setDirection will change the value. Nothing about
4717 * a default value when the transceiver is created internally */
4720 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
4721 _get_direction_from_media (media), i));
4724 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
4725 trans, bundled, bundle_idx, error);
4726 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
4727 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
4730 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
4735 if (bundle_stream && bundle_stream->active == FALSE) {
4736 /* No bundled mline marked the bundle as active, so block the receive bin, as
4737 * this bundle is completely inactive */
4738 GST_LOG_OBJECT (webrtc,
4739 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
4740 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
4741 RECEIVE_STATE_BLOCK);
4747 g_strfreev (bundled);
4753 check_transceivers_not_removed (GstWebRTCBin * webrtc,
4754 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
4759 if (gst_sdp_message_medias_len (previous->sdp) >
4760 gst_sdp_message_medias_len (new->sdp))
4766 struct set_description
4768 GstPromise *promise;
4770 GstWebRTCSessionDescription *sdp;
4773 static GstWebRTCSessionDescription *
4774 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
4775 GstWebRTCSDPType type)
4778 case GST_WEBRTC_SDP_TYPE_OFFER:
4779 case GST_WEBRTC_SDP_TYPE_PRANSWER:
4780 case GST_WEBRTC_SDP_TYPE_ANSWER:
4781 if (source == SDP_LOCAL) {
4782 return webrtc->current_local_description;
4784 return webrtc->current_remote_description;
4786 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
4789 /* other values mean memory corruption/uninitialized! */
4790 g_assert_not_reached ();
4797 /* http://w3c.github.io/webrtc-pc/#set-description */
4799 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
4801 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
4802 gboolean signalling_state_changed = FALSE;
4803 GError *error = NULL;
4804 GStrv bundled = NULL;
4805 guint bundle_idx = 0;
4809 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4810 webrtc->signaling_state);
4812 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
4813 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
4814 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
4815 _sdp_source_to_string (sd->source), type_str, state);
4816 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
4822 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
4825 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
4826 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
4830 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
4831 g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4832 "Bundle tag is %s but no matching media found", bundled[0]);
4837 if (!check_transceivers_not_removed (webrtc,
4838 get_previous_description (webrtc, sd->source, sd->sdp->type),
4840 g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR,
4841 GST_WEBRTC_BIN_ERROR_BAD_SDP,
4842 "m=lines removed from the SDP. Processing a completely new connection "
4843 "is not currently supported.");
4847 switch (sd->sdp->type) {
4848 case GST_WEBRTC_SDP_TYPE_OFFER:{
4849 if (sd->source == SDP_LOCAL) {
4850 if (webrtc->pending_local_description)
4851 gst_webrtc_session_description_free
4852 (webrtc->pending_local_description);
4853 webrtc->pending_local_description =
4854 gst_webrtc_session_description_copy (sd->sdp);
4855 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
4857 if (webrtc->pending_remote_description)
4858 gst_webrtc_session_description_free
4859 (webrtc->pending_remote_description);
4860 webrtc->pending_remote_description =
4861 gst_webrtc_session_description_copy (sd->sdp);
4862 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
4866 case GST_WEBRTC_SDP_TYPE_ANSWER:{
4867 if (sd->source == SDP_LOCAL) {
4868 if (webrtc->current_local_description)
4869 gst_webrtc_session_description_free
4870 (webrtc->current_local_description);
4871 webrtc->current_local_description =
4872 gst_webrtc_session_description_copy (sd->sdp);
4874 if (webrtc->current_remote_description)
4875 gst_webrtc_session_description_free
4876 (webrtc->current_remote_description);
4877 webrtc->current_remote_description = webrtc->pending_remote_description;
4878 webrtc->pending_remote_description = NULL;
4880 if (webrtc->current_remote_description)
4881 gst_webrtc_session_description_free
4882 (webrtc->current_remote_description);
4883 webrtc->current_remote_description =
4884 gst_webrtc_session_description_copy (sd->sdp);
4886 if (webrtc->current_local_description)
4887 gst_webrtc_session_description_free
4888 (webrtc->current_local_description);
4889 webrtc->current_local_description = webrtc->pending_local_description;
4890 webrtc->pending_local_description = NULL;
4893 if (webrtc->pending_local_description)
4894 gst_webrtc_session_description_free (webrtc->pending_local_description);
4895 webrtc->pending_local_description = NULL;
4897 if (webrtc->pending_remote_description)
4898 gst_webrtc_session_description_free
4899 (webrtc->pending_remote_description);
4900 webrtc->pending_remote_description = NULL;
4902 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4905 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
4906 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
4907 if (sd->source == SDP_LOCAL) {
4908 if (webrtc->pending_local_description)
4909 gst_webrtc_session_description_free
4910 (webrtc->pending_local_description);
4911 webrtc->pending_local_description = NULL;
4913 if (webrtc->pending_remote_description)
4914 gst_webrtc_session_description_free
4915 (webrtc->pending_remote_description);
4916 webrtc->pending_remote_description = NULL;
4919 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4922 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
4923 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
4924 if (sd->source == SDP_LOCAL) {
4925 if (webrtc->pending_local_description)
4926 gst_webrtc_session_description_free
4927 (webrtc->pending_local_description);
4928 webrtc->pending_local_description =
4929 gst_webrtc_session_description_copy (sd->sdp);
4931 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
4933 if (webrtc->pending_remote_description)
4934 gst_webrtc_session_description_free
4935 (webrtc->pending_remote_description);
4936 webrtc->pending_remote_description =
4937 gst_webrtc_session_description_copy (sd->sdp);
4939 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
4945 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
4947 * If the mid value of an RTCRtpTransceiver was set to a non-null value
4948 * by the RTCSessionDescription that is being rolled back, set the mid
4949 * value of that transceiver to null, as described by [JSEP]
4950 * (section 4.1.7.2.).
4951 * If an RTCRtpTransceiver was created by applying the
4952 * RTCSessionDescription that is being rolled back, and a track has not
4953 * been attached to it via addTrack, remove that transceiver from
4954 * connection's set of transceivers, as described by [JSEP]
4955 * (section 4.1.7.2.).
4956 * Restore the value of connection's [[ sctpTransport]] internal slot
4957 * to its value at the last stable signaling state.
4961 if (webrtc->signaling_state != new_signaling_state) {
4962 webrtc->signaling_state = new_signaling_state;
4963 signalling_state_changed = TRUE;
4967 gboolean ice_controller = FALSE;
4969 /* get the current value so we don't change ice controller from TRUE to
4970 * FALSE on renegotiation or once set to TRUE for the initial local offer */
4971 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
4973 /* we control ice negotiation if we send the initial offer */
4975 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
4976 && webrtc->current_remote_description == NULL;
4977 /* or, if the remote is an ice-lite peer */
4978 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
4979 && webrtc->current_remote_description
4980 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
4983 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
4984 ice_controller ? "true" : "false");
4985 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
4988 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
4991 /* media modifications */
4992 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
4995 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
4996 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
4997 GstWebRTCRTPTransceiverDirection new_dir;
4999 const GstSDPMedia *media;
5001 if (!pad->received_caps) {
5002 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
5007 if (pad->mlineindex >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
5008 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
5013 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->mlineindex);
5014 /* skip rejected media */
5015 if (gst_sdp_media_get_port (media) == 0) {
5016 /* FIXME: arrange for an appropriate flow return */
5017 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
5018 "a more correct flow return.");
5024 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
5029 new_dir = pad->trans->direction;
5030 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
5031 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5032 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
5033 "data at the moment. Not connecting input stream yet", pad->trans);
5038 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
5039 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
5040 pad->trans, pad->received_caps);
5041 _connect_input_stream (webrtc, pad);
5042 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
5046 gst_object_unref (old->data);
5047 webrtc->priv->pending_sink_transceivers =
5048 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
5052 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
5053 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
5055 TransportStream *item;
5058 _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
5059 _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
5061 if (sd->source == SDP_REMOTE) {
5064 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
5065 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
5067 if (g_strcmp0 (attr->key, "ssrc") == 0) {
5068 GStrv split = g_strsplit (attr->value, " ", 0);
5071 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
5072 && g_str_has_prefix (split[1], "cname:")) {
5073 g_ptr_array_add (item->remote_ssrcmap, ssrcmap_item_new (ssrc, i));
5080 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
5081 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5083 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
5084 item->stream, ufrag, pwd);
5087 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
5088 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5090 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
5091 item->stream, ufrag, pwd);
5097 if (sd->source == SDP_LOCAL) {
5098 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
5099 IceStreamItem *item =
5100 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
5102 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
5106 /* Add any pending trickle ICE candidates if we have both offer and answer */
5107 if (webrtc->current_local_description && webrtc->current_remote_description) {
5110 GstWebRTCSessionDescription *remote_sdp =
5111 webrtc->current_remote_description;
5113 /* Add any remote ICE candidates from the remote description to
5114 * support non-trickle peers first */
5115 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
5116 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
5117 _add_ice_candidates_from_sdp (webrtc, i, media);
5121 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
5122 IceCandidateItem *item =
5123 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
5124 IceCandidateItem, i);
5126 _add_ice_candidate (webrtc, item, TRUE);
5128 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
5129 ICE_UNLOCK (webrtc);
5133 * If connection's signaling state changed above, fire an event named
5134 * signalingstatechange at connection.
5136 if (signalling_state_changed) {
5137 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5138 webrtc->signaling_state);
5139 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5140 new_signaling_state);
5141 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
5144 g_object_notify (G_OBJECT (webrtc), "signaling-state");
5151 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5152 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
5154 /* If connection's signaling state is now stable, update the
5155 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
5156 * was true both before and after this update, queue a task to check
5157 * connection's [[needNegotiation]] slot and, if still true, fire a
5158 * simple event named negotiationneeded at connection.*/
5159 _update_need_negotiation (webrtc);
5160 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
5161 _check_need_negotiation_task (webrtc, NULL);
5166 g_strfreev (bundled);
5170 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
5171 gst_promise_reply (sd->promise,
5172 gst_structure_new ("application/x-getwebrtcbin-error", "error",
5173 G_TYPE_ERROR, error, NULL));
5174 g_clear_error (&error);
5176 gst_promise_reply (sd->promise, NULL);
5182 _free_set_description_data (struct set_description *sd)
5185 gst_promise_unref (sd->promise);
5187 gst_webrtc_session_description_free (sd->sdp);
5192 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
5193 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
5195 struct set_description *sd;
5197 if (remote_sdp == NULL)
5199 if (remote_sdp->sdp == NULL)
5202 sd = g_new0 (struct set_description, 1);
5203 if (promise != NULL)
5204 sd->promise = gst_promise_ref (promise);
5205 sd->source = SDP_REMOTE;
5206 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
5208 if (!gst_webrtc_bin_enqueue_task (webrtc,
5209 (GstWebRTCBinFunc) _set_description_task, sd,
5210 (GDestroyNotify) _free_set_description_data, promise)) {
5212 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5213 "Could not set remote description. webrtcbin is closed.");
5215 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
5216 "error", G_TYPE_ERROR, error, NULL);
5218 gst_promise_reply (promise, s);
5220 g_clear_error (&error);
5227 gst_promise_reply (promise, NULL);
5228 g_return_if_reached ();
5233 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
5234 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
5236 struct set_description *sd;
5238 if (local_sdp == NULL)
5240 if (local_sdp->sdp == NULL)
5243 sd = g_new0 (struct set_description, 1);
5244 if (promise != NULL)
5245 sd->promise = gst_promise_ref (promise);
5246 sd->source = SDP_LOCAL;
5247 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
5249 if (!gst_webrtc_bin_enqueue_task (webrtc,
5250 (GstWebRTCBinFunc) _set_description_task, sd,
5251 (GDestroyNotify) _free_set_description_data, promise)) {
5253 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5254 "Could not set remote description. webrtcbin is closed");
5256 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
5257 "error", G_TYPE_ERROR, error, NULL);
5259 gst_promise_reply (promise, s);
5261 g_clear_error (&error);
5268 gst_promise_reply (promise, NULL);
5269 g_return_if_reached ();
5274 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
5276 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
5277 IceCandidateItem new;
5278 new.mlineindex = item->mlineindex;
5279 new.candidate = g_steal_pointer (&item->candidate);
5282 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5283 ICE_UNLOCK (webrtc);
5285 _add_ice_candidate (webrtc, item, FALSE);
5290 _free_ice_candidate_item (IceCandidateItem * item)
5292 _clear_ice_candidate_item (item);
5297 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
5300 IceCandidateItem *item;
5302 item = g_new0 (IceCandidateItem, 1);
5303 item->mlineindex = mline;
5304 if (attr && attr[0] != 0) {
5305 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
5306 item->candidate = g_strdup (attr);
5307 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
5308 item->candidate = g_strdup_printf ("a=%s", attr);
5310 gst_webrtc_bin_enqueue_task (webrtc,
5311 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
5312 (GDestroyNotify) _free_ice_candidate_item, NULL);
5316 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
5322 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
5323 ICE_UNLOCK (webrtc);
5324 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
5325 return; /* Nothing to process */
5327 /* Take the array so we can process it all and free it later
5328 * without holding the lock
5329 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
5331 items = webrtc->priv->pending_local_ice_candidates;
5332 /* Replace with a new array */
5333 webrtc->priv->pending_local_ice_candidates =
5334 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
5335 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
5336 (GDestroyNotify) _clear_ice_candidate_item);
5337 ICE_UNLOCK (webrtc);
5339 for (i = 0; i < items->len; i++) {
5340 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
5341 const gchar *cand = item->candidate;
5343 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
5344 /* stripping away "a=" */
5348 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
5349 item->mlineindex, cand);
5351 /* First, merge this ice candidate into the appropriate mline
5352 * in the local-description SDP.
5353 * Second, emit the on-ice-candidate signal for the app.
5355 * FIXME: This ICE candidate should be stored somewhere with
5356 * the associated mid and also merged back into any subsequent
5357 * local descriptions on renegotiation */
5358 if (webrtc->current_local_description)
5359 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
5360 item->mlineindex, cand);
5361 if (webrtc->pending_local_description)
5362 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
5363 item->mlineindex, cand);
5366 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
5367 0, item->mlineindex, cand);
5371 g_array_free (items, TRUE);
5373 if (webrtc->pending_ice_gathering_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
5374 _update_and_notify_ice_gathering_state (webrtc, GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE);
5375 webrtc->pending_ice_gathering_state = GST_WEBRTC_ICE_GATHERING_STATE_NEW;
5381 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
5382 gchar * candidate, GstWebRTCBin * webrtc)
5384 IceCandidateItem item;
5385 gboolean queue_task = FALSE;
5387 item.mlineindex = session_id;
5388 item.candidate = g_strdup (candidate);
5391 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
5393 /* Let the first pending candidate queue a task each time, which will
5394 * handle any that arrive between now and when the task runs */
5395 if (webrtc->priv->pending_local_ice_candidates->len == 1)
5397 ICE_UNLOCK (webrtc);
5400 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
5401 gst_webrtc_bin_enqueue_task (webrtc,
5402 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
5409 GstPromise *promise;
5413 _free_get_stats (struct get_stats *stats)
5416 gst_object_unref (stats->pad);
5418 gst_promise_unref (stats->promise);
5422 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
5424 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
5426 /* Our selector is the pad,
5427 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
5429 gst_promise_reply (stats->promise, gst_webrtc_bin_create_stats (webrtc,
5434 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
5435 GstPromise * promise)
5437 struct get_stats *stats;
5439 g_return_if_fail (promise != NULL);
5440 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
5442 stats = g_new0 (struct get_stats, 1);
5443 stats->promise = gst_promise_ref (promise);
5444 /* FIXME: check that pad exists in element */
5446 stats->pad = gst_object_ref (pad);
5448 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
5449 stats, (GDestroyNotify) _free_get_stats, promise)) {
5451 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5452 "Could not retrieve statistics. webrtcbin is closed.");
5453 GstStructure *s = gst_structure_new ("application/x-gst-promise-error",
5454 "error", G_TYPE_ERROR, error, NULL);
5456 gst_promise_reply (promise, s);
5458 g_clear_error (&error);
5462 static GstWebRTCRTPTransceiver *
5463 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
5464 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
5466 WebRTCTransceiver *trans;
5467 GstWebRTCRTPTransceiver *rtp_trans;
5469 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
5472 trans = _create_webrtc_transceiver (webrtc, direction, -1);
5473 GST_LOG_OBJECT (webrtc,
5474 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
5476 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
5478 rtp_trans->codec_preferences = gst_caps_ref (caps);
5479 _update_transceiver_kind_from_caps (rtp_trans, caps);
5482 return gst_object_ref (trans);
5486 _deref_and_unref (GstObject ** object)
5488 gst_clear_object (object);
5492 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
5494 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
5497 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
5499 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5500 GstWebRTCRTPTransceiver *trans =
5501 g_ptr_array_index (webrtc->priv->transceivers, i);
5502 gst_object_ref (trans);
5503 g_array_append_val (arr, trans);
5509 static GstWebRTCRTPTransceiver *
5510 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
5512 GstWebRTCRTPTransceiver *trans = NULL;
5514 if (idx >= webrtc->priv->transceivers->len) {
5515 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
5519 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
5520 gst_object_ref (trans);
5527 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
5529 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
5530 g_return_val_if_fail (uri != NULL, FALSE);
5532 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
5534 return gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
5538 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
5540 GstPad *gpad = GST_PAD_CAST (user_data);
5542 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
5543 gst_pad_store_sticky_event (gpad, *event);
5548 static WebRTCDataChannel *
5549 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
5550 GstStructure * init_params)
5553 gint max_packet_lifetime;
5554 gint max_retransmits;
5555 const gchar *protocol;
5556 gboolean negotiated;
5558 GstWebRTCPriorityType priority;
5559 WebRTCDataChannel *ret;
5560 gint max_channels = 65534;
5562 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
5563 g_return_val_if_fail (label != NULL, NULL);
5564 g_return_val_if_fail (strlen (label) <= 65535, NULL);
5566 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
5570 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
5573 || !gst_structure_get_int (init_params, "max-packet-lifetime",
5574 &max_packet_lifetime))
5575 max_packet_lifetime = -1;
5577 || !gst_structure_get_int (init_params, "max-retransmits",
5579 max_retransmits = -1;
5580 /* both retransmits and lifetime cannot be set */
5581 g_return_val_if_fail ((max_packet_lifetime == -1)
5582 || (max_retransmits == -1), NULL);
5585 || !(protocol = gst_structure_get_string (init_params, "protocol")))
5587 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
5590 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
5592 if (!negotiated || !init_params
5593 || !gst_structure_get_int (init_params, "id", &id))
5596 g_return_val_if_fail (id != -1, NULL);
5597 g_return_val_if_fail (id < 65535, NULL);
5600 || !gst_structure_get_enum (init_params, "priority",
5601 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
5602 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
5604 /* FIXME: clamp max-retransmits and max-packet-lifetime */
5606 if (webrtc->priv->sctp_transport) {
5607 /* Let transport be the connection's [[SctpTransport]] slot.
5609 * If the [[DataChannelId]] slot is not null, transport is in
5610 * connected state and [[DataChannelId]] is greater or equal to the
5611 * transport's [[MaxChannels]] slot, throw an OperationError.
5613 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5616 g_return_val_if_fail (id <= max_channels, NULL);
5619 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
5620 !_have_sctp_elements (webrtc))
5624 /* check if the id has been used already */
5626 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
5628 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
5629 ("Attempting to add a data channel with a duplicate ID: %i", id),
5634 } else if (webrtc->current_local_description
5635 && webrtc->current_remote_description && webrtc->priv->sctp_transport
5636 && webrtc->priv->sctp_transport->transport) {
5637 /* else we can only generate an id if we're configured already. The other
5638 * case for generating an id is on sdp setting */
5639 id = _generate_data_channel_id (webrtc);
5641 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5642 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5648 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
5649 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
5650 "max-retransmits", max_retransmits, "protocol", protocol,
5651 "negotiated", negotiated, "id", id, "priority", priority, NULL);
5654 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
5655 gst_bin_add (GST_BIN (webrtc), ret->appsink);
5657 gst_element_sync_state_with_parent (ret->appsrc);
5658 gst_element_sync_state_with_parent (ret->appsink);
5660 ret = gst_object_ref (ret);
5661 ret->webrtcbin = webrtc;
5662 g_ptr_array_add (webrtc->priv->data_channels, ret);
5663 gst_webrtc_bin_update_sctp_priority (webrtc);
5664 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
5665 if (webrtc->priv->sctp_transport &&
5666 webrtc->priv->sctp_transport->association_established
5667 && !ret->parent.negotiated) {
5668 webrtc_data_channel_start_negotiation (ret);
5670 _update_need_negotiation (webrtc);
5678 /* === rtpbin signal implementations === */
5681 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
5682 GstWebRTCBin * webrtc)
5684 gchar *new_pad_name = NULL;
5686 new_pad_name = gst_pad_get_name (new_pad);
5687 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
5688 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
5689 guint32 session_id = 0, ssrc = 0, pt = 0;
5690 GstWebRTCRTPTransceiver *rtp_trans;
5691 WebRTCTransceiver *trans;
5692 TransportStream *stream;
5693 GstWebRTCBinPad *pad;
5694 guint media_idx = 0;
5695 gboolean found_ssrc = FALSE;
5698 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
5700 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
5704 stream = _find_transport_for_session (webrtc, session_id);
5706 g_warn_if_reached ();
5708 media_idx = session_id;
5710 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
5711 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
5712 if (item->ssrc == ssrc) {
5713 media_idx = item->media_idx;
5720 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
5723 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
5725 g_warn_if_reached ();
5726 trans = WEBRTC_TRANSCEIVER (rtp_trans);
5727 g_assert (trans->stream == stream);
5729 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5731 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
5732 " for rtpbin pad name %s", pad, new_pad_name);
5734 g_warn_if_reached ();
5735 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
5737 if (webrtc->priv->running)
5738 gst_pad_set_active (GST_PAD (pad), TRUE);
5739 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
5740 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
5741 _remove_pending_pad (webrtc, pad);
5743 gst_object_unref (pad);
5745 g_free (new_pad_name);
5748 /* only used for the receiving streams */
5750 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
5751 GstWebRTCBin * webrtc)
5753 TransportStream *stream;
5756 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
5759 stream = _find_transport_for_session (webrtc, session_id);
5761 goto unknown_session;
5763 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
5766 GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
5767 "session %d", ret, pt, session_id);
5773 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
5779 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
5781 GstStructure *s = user_data;
5783 gst_structure_id_set_value (s, field_id, value);
5789 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
5790 GstWebRTCBin * webrtc)
5792 TransportStream *stream;
5793 gboolean have_rtx = FALSE;
5795 GstStructure *pt_map = NULL;
5797 GstElement *ret = NULL;
5799 stream = _find_transport_for_session (webrtc, session_id);
5802 have_rtx = transport_stream_get_pt (stream, "RTX") != 0;
5805 GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
5806 " with pt map %" GST_PTR_FORMAT, stream, pt_map);
5808 GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT, stream);
5815 GstStructure *merged_local_rtx_ssrc_map =
5816 gst_structure_new_empty ("application/x-rtp-ssrc-map");
5819 if (stream->rtxsend) {
5820 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
5824 GST_INFO ("creating AUX sender");
5825 ret = gst_bin_new (NULL);
5826 rtx = gst_element_factory_make ("rtprtxsend", NULL);
5827 g_object_set (rtx, "max-size-packets", 500, NULL);
5828 _set_rtx_ptmap_from_stream (webrtc, stream);
5830 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5831 WebRTCTransceiver *trans =
5832 WEBRTC_TRANSCEIVER (g_ptr_array_index (webrtc->priv->transceivers,
5835 if (trans->stream == stream && trans->local_rtx_ssrc_map)
5836 gst_structure_foreach (trans->local_rtx_ssrc_map,
5837 _merge_structure, merged_local_rtx_ssrc_map);
5840 g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
5841 gst_structure_free (merged_local_rtx_ssrc_map);
5843 gst_bin_add (GST_BIN (ret), rtx);
5845 pad = gst_element_get_static_pad (rtx, "src");
5846 name = g_strdup_printf ("src_%u", session_id);
5847 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
5849 gst_object_unref (pad);
5851 pad = gst_element_get_static_pad (rtx, "sink");
5852 name = g_strdup_printf ("sink_%u", session_id);
5853 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
5855 gst_object_unref (pad);
5857 stream->rtxsend = gst_object_ref (rtx);
5863 gst_structure_free (pt_map);
5870 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
5871 GstWebRTCBin * webrtc)
5873 GstElement *ret = NULL;
5874 GstElement *prev = NULL;
5875 GstPad *sinkpad = NULL;
5876 TransportStream *stream;
5880 stream = _find_transport_for_session (webrtc, session_id);
5883 red_pt = transport_stream_get_pt (stream, "RED");
5884 rtx_pt = transport_stream_get_pt (stream, "RTX");
5887 GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
5890 if (red_pt || rtx_pt)
5891 ret = gst_bin_new (NULL);
5894 if (stream->rtxreceive) {
5895 GST_WARNING_OBJECT (webrtc,
5896 "rtprtxreceive already created! rtpbin bug?!");
5900 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
5901 _set_rtx_ptmap_from_stream (webrtc, stream);
5903 gst_bin_add (GST_BIN (ret), stream->rtxreceive);
5905 sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
5907 prev = gst_object_ref (stream->rtxreceive);
5911 GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
5913 GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
5914 red_pt, session_id);
5916 gst_bin_add (GST_BIN (ret), rtpreddec);
5918 g_object_set (rtpreddec, "pt", red_pt, NULL);
5921 gst_element_link (prev, rtpreddec);
5923 sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
5929 gchar *name = g_strdup_printf ("sink_%u", session_id);
5930 GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
5932 gst_object_unref (sinkpad);
5933 gst_element_add_pad (ret, ghost);
5937 gchar *name = g_strdup_printf ("src_%u", session_id);
5938 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
5939 GstPad *ghost = gst_ghost_pad_new (name, srcpad);
5941 gst_object_unref (srcpad);
5942 gst_element_add_pad (ret, ghost);
5950 gst_object_unref (ret);
5955 on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
5956 GstWebRTCBin * webrtc)
5958 TransportStream *stream;
5959 GstElement *ret = NULL;
5961 GObject *internal_storage;
5963 stream = _find_transport_for_session (webrtc, session_id);
5965 /* TODO: for now, we only support ulpfec, but once we support
5966 * more algorithms, if the remote may use more than one algorithm,
5967 * we will want to do the following:
5969 * + Return a bin here, with the relevant FEC decoders plugged in
5970 * and their payload type set to 0
5971 * + Enable the decoders by setting the payload type only when
5972 * we detect it (by connecting to ptdemux:new-payload-type for
5976 pt = transport_stream_get_pt (stream, "ULPFEC");
5979 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
5981 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
5982 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
5985 g_object_set (ret, "pt", pt, "storage", internal_storage, NULL);
5986 g_object_unref (internal_storage);
5993 on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
5994 GstWebRTCBin * webrtc)
5996 GstElement *ret = NULL;
5997 GstElement *prev = NULL;
5998 TransportStream *stream;
5999 guint ulpfec_pt = 0;
6001 GstPad *sinkpad = NULL;
6002 GstWebRTCRTPTransceiver *trans;
6004 stream = _find_transport_for_session (webrtc, session_id);
6005 trans = _find_transceiver (webrtc, &session_id,
6006 (FindTransceiverFunc) transceiver_match_for_mline);
6009 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
6010 red_pt = transport_stream_get_pt (stream, "RED");
6013 if (ulpfec_pt || red_pt)
6014 ret = gst_bin_new (NULL);
6017 GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
6018 GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
6020 GST_DEBUG_OBJECT (webrtc,
6021 "Creating ULPFEC encoder for session %d with pt %d", session_id,
6024 gst_bin_add (GST_BIN (ret), fecenc);
6025 sinkpad = gst_element_get_static_pad (fecenc, "sink");
6026 g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
6027 WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
6030 if (caps && !gst_caps_is_empty (caps)) {
6031 const GstStructure *s = gst_caps_get_structure (caps, 0);
6032 const gchar *media = gst_structure_get_string (s, "media");
6034 if (!g_strcmp0 (media, "video"))
6035 g_object_set (fecenc, "multipacket", TRUE, NULL);
6042 GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
6044 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
6045 session_id, red_pt);
6047 gst_bin_add (GST_BIN (ret), redenc);
6049 gst_element_link (prev, redenc);
6051 sinkpad = gst_element_get_static_pad (redenc, "sink");
6053 g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
6059 GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
6060 gst_object_unref (sinkpad);
6061 gst_element_add_pad (ret, ghost);
6065 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
6066 GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
6067 gst_object_unref (srcpad);
6068 gst_element_add_pad (ret, ghost);
6075 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6076 GstWebRTCBin * webrtc)
6078 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
6082 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6083 GstWebRTCBin * webrtc)
6085 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
6089 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6090 GstWebRTCBin * webrtc)
6092 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
6097 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6098 GstWebRTCBin * webrtc)
6100 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
6104 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6105 GstWebRTCBin * webrtc)
6107 GST_INFO_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
6111 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
6112 GstWebRTCBin * webrtc)
6114 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
6118 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
6119 GstWebRTCBin * webrtc)
6121 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
6125 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
6126 GstWebRTCBin * webrtc)
6128 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
6132 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6133 GstWebRTCBin * webrtc)
6135 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
6139 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6140 GstWebRTCBin * webrtc)
6142 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
6147 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6148 GstWebRTCBin * webrtc)
6150 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
6155 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
6156 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
6158 WebRTCTransceiver *trans;
6161 trans = (WebRTCTransceiver *) _find_transceiver (webrtc, &session_id,
6162 (FindTransceiverFunc) transceiver_match_for_mline);
6165 /* We don't set do-retransmission on rtpbin as we want per-session control */
6166 g_object_set (jitterbuffer, "do-retransmission",
6167 WEBRTC_TRANSCEIVER (trans)->do_nack, NULL);
6169 for (i = 0; i < trans->stream->remote_ssrcmap->len; i++) {
6170 SsrcMapItem *item = g_ptr_array_index (trans->stream->remote_ssrcmap, i);
6172 if (item->ssrc == ssrc) {
6173 g_weak_ref_set (&item->rtpjitterbuffer, jitterbuffer);
6178 g_assert_not_reached ();
6183 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
6184 guint session_id, GstWebRTCBin * webrtc)
6186 guint64 latency = webrtc->priv->jb_latency;
6188 /* Add an extra 50 ms for safey */
6189 latency += RTPSTORAGE_EXTRA_TIME;
6190 latency *= GST_MSECOND;
6192 g_object_set (storage, "size-time", latency, NULL);
6196 _create_rtpbin (GstWebRTCBin * webrtc)
6200 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
6203 /* mandated by WebRTC */
6204 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
6206 g_object_set (rtpbin, "do-lost", TRUE, NULL);
6208 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
6210 g_signal_connect (rtpbin, "request-pt-map",
6211 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
6212 g_signal_connect (rtpbin, "request-aux-sender",
6213 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
6214 g_signal_connect (rtpbin, "request-aux-receiver",
6215 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
6216 g_signal_connect (rtpbin, "new-storage",
6217 G_CALLBACK (on_rtpbin_new_storage), webrtc);
6218 g_signal_connect (rtpbin, "request-fec-decoder",
6219 G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
6220 g_signal_connect (rtpbin, "request-fec-encoder",
6221 G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
6222 g_signal_connect (rtpbin, "on-bye-ssrc",
6223 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
6224 g_signal_connect (rtpbin, "on-bye-timeout",
6225 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
6226 g_signal_connect (rtpbin, "on-new-ssrc",
6227 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
6228 g_signal_connect (rtpbin, "on-new-sender-ssrc",
6229 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
6230 g_signal_connect (rtpbin, "on-sender-ssrc-active",
6231 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
6232 g_signal_connect (rtpbin, "on-sender-timeout",
6233 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
6234 g_signal_connect (rtpbin, "on-ssrc-active",
6235 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
6236 g_signal_connect (rtpbin, "on-ssrc-collision",
6237 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
6238 g_signal_connect (rtpbin, "on-ssrc-sdes",
6239 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
6240 g_signal_connect (rtpbin, "on-ssrc-validated",
6241 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
6242 g_signal_connect (rtpbin, "on-timeout",
6243 G_CALLBACK (on_rtpbin_timeout), webrtc);
6244 g_signal_connect (rtpbin, "new-jitterbuffer",
6245 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
6250 static GstStateChangeReturn
6251 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
6253 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6254 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
6256 GST_DEBUG ("changing state: %s => %s",
6257 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
6258 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
6260 switch (transition) {
6261 case GST_STATE_CHANGE_NULL_TO_READY:{
6262 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6263 return GST_STATE_CHANGE_FAILURE;
6264 _start_thread (webrtc);
6266 _update_need_negotiation (webrtc);
6270 case GST_STATE_CHANGE_READY_TO_PAUSED:
6271 webrtc->priv->running = TRUE;
6277 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6278 if (ret == GST_STATE_CHANGE_FAILURE)
6281 switch (transition) {
6282 case GST_STATE_CHANGE_READY_TO_PAUSED:
6283 /* Mangle the return value to NO_PREROLL as that's what really is
6284 * occurring here however cannot be propagated correctly due to nicesrc
6285 * requiring that it be in PLAYING already in order to send/receive
6287 ret = GST_STATE_CHANGE_NO_PREROLL;
6289 case GST_STATE_CHANGE_PAUSED_TO_READY:
6290 webrtc->priv->running = FALSE;
6292 case GST_STATE_CHANGE_READY_TO_NULL:
6293 _stop_thread (webrtc);
6295 webrtc->priv->need_negotiation = FALSE;
6305 static GstPadProbeReturn
6306 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
6308 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
6310 return GST_PAD_PROBE_OK;
6314 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
6315 const gchar * name, const GstCaps * caps)
6317 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6318 GstWebRTCBinPad *pad = NULL;
6321 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6324 if (templ->direction == GST_PAD_SINK ||
6325 g_strcmp0 (templ->name_template, "sink_%u") == 0) {
6326 GstWebRTCRTPTransceiver *trans;
6328 GST_OBJECT_LOCK (webrtc);
6329 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
6330 /* no name given when requesting the pad, use next available int */
6331 serial = webrtc->priv->max_sink_pad_serial++;
6333 /* parse serial number from requested padname */
6334 serial = g_ascii_strtoull (&name[5], NULL, 10);
6335 if (serial > webrtc->priv->max_sink_pad_serial)
6336 webrtc->priv->max_sink_pad_serial = serial;
6338 GST_OBJECT_UNLOCK (webrtc);
6340 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial);
6341 trans = _find_transceiver_for_mline (webrtc, serial);
6344 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
6345 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, serial));
6346 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
6347 " for mline %u", trans, serial);
6349 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
6350 " for mline %u", trans, serial);
6352 pad->trans = gst_object_ref (trans);
6354 if (caps && name && !_update_transceiver_kind_from_caps (trans, caps))
6355 GST_WARNING_OBJECT (webrtc,
6356 "Trying to create pad %s with caps %" GST_PTR_FORMAT
6357 " but transceiver %d already exists with a different"
6358 " media type", name, caps, serial);
6360 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
6361 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
6362 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
6363 webrtc->priv->pending_sink_transceivers =
6364 g_list_append (webrtc->priv->pending_sink_transceivers,
6365 gst_object_ref (pad));
6366 _add_pad (webrtc, pad);
6369 return GST_PAD (pad);
6373 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
6375 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6376 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
6378 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
6380 /* remove the transceiver from the pad so that subsequent code doesn't use
6381 * a possibly dead transceiver */
6383 if (webrtc_pad->trans)
6385 _remove_webrtc_transceiver (webrtc, webrtc_pad->trans);
6387 gst_object_unref (webrtc_pad->trans);
6389 webrtc_pad->trans = NULL;
6392 _remove_pad (webrtc, webrtc_pad);
6395 _update_need_negotiation (webrtc);
6400 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
6405 /* Add an extra 50 ms for safety */
6406 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
6407 latency_ns *= GST_MSECOND;
6409 for (i = 0; i < webrtc->priv->transports->len; i++) {
6410 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
6411 GObject *storage = NULL;
6413 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
6416 g_object_set (storage, "size-time", latency_ns, NULL);
6418 g_object_unref (storage);
6422 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
6424 _update_drop_probability (GstWebRTCBin * webrtc, gfloat probability)
6426 GValue value = G_VALUE_INIT;
6427 GstElement *element;
6428 GstIterator *bin_iterator = gst_bin_iterate_sorted (GST_BIN (webrtc));
6429 g_assert (bin_iterator);
6431 while (gst_iterator_next (bin_iterator, &value) == GST_ITERATOR_OK) {
6432 element = GST_ELEMENT (g_value_get_object (&value));
6433 if (g_strrstr (GST_ELEMENT_NAME (element), "netsim"))
6434 g_object_set (element, "drop-probability", probability, NULL);
6435 g_value_reset (&value);
6438 g_value_unset (&value);
6439 gst_iterator_free (bin_iterator);
6444 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
6445 const GValue * value, GParamSpec * pspec)
6447 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6450 case PROP_STUN_SERVER:
6451 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
6452 g_value_get_string (value));
6454 case PROP_TURN_SERVER:
6455 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
6456 g_value_get_string (value));
6458 case PROP_BUNDLE_POLICY:
6459 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
6460 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
6462 webrtc->bundle_policy = g_value_get_enum (value);
6465 case PROP_ICE_TRANSPORT_POLICY:
6466 webrtc->ice_transport_policy = g_value_get_enum (value);
6467 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
6468 webrtc->ice_transport_policy ==
6469 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
6472 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
6473 webrtc->priv->jb_latency = g_value_get_uint (value);
6474 _update_rtpstorage_latency (webrtc);
6476 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
6478 webrtc->priv->netsim = g_value_get_boolean (value);
6479 _update_drop_probability (webrtc, webrtc->priv->netsim ?
6480 webrtc->priv->drop_probability_sender : 0.0);
6482 case PROP_DROP_PROBABILITY_SENDER:
6483 webrtc->priv->drop_probability_sender = g_value_get_float (value);
6484 _update_drop_probability (webrtc, webrtc->priv->drop_probability_sender);
6488 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
6494 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
6495 GValue * value, GParamSpec * pspec)
6497 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6501 case PROP_CONNECTION_STATE:
6502 g_value_set_enum (value, webrtc->peer_connection_state);
6504 case PROP_SIGNALING_STATE:
6505 g_value_set_enum (value, webrtc->signaling_state);
6507 case PROP_ICE_GATHERING_STATE:
6508 g_value_set_enum (value, webrtc->ice_gathering_state);
6510 case PROP_ICE_CONNECTION_STATE:
6511 g_value_set_enum (value, webrtc->ice_connection_state);
6513 case PROP_LOCAL_DESCRIPTION:
6514 if (webrtc->pending_local_description)
6515 g_value_set_boxed (value, webrtc->pending_local_description);
6516 else if (webrtc->current_local_description)
6517 g_value_set_boxed (value, webrtc->current_local_description);
6519 g_value_set_boxed (value, NULL);
6521 case PROP_CURRENT_LOCAL_DESCRIPTION:
6522 g_value_set_boxed (value, webrtc->current_local_description);
6524 case PROP_PENDING_LOCAL_DESCRIPTION:
6525 g_value_set_boxed (value, webrtc->pending_local_description);
6527 case PROP_REMOTE_DESCRIPTION:
6528 if (webrtc->pending_remote_description)
6529 g_value_set_boxed (value, webrtc->pending_remote_description);
6530 else if (webrtc->current_remote_description)
6531 g_value_set_boxed (value, webrtc->current_remote_description);
6533 g_value_set_boxed (value, NULL);
6535 case PROP_CURRENT_REMOTE_DESCRIPTION:
6536 g_value_set_boxed (value, webrtc->current_remote_description);
6538 case PROP_PENDING_REMOTE_DESCRIPTION:
6539 g_value_set_boxed (value, webrtc->pending_remote_description);
6541 case PROP_STUN_SERVER:
6542 g_value_take_string (value,
6543 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
6545 case PROP_TURN_SERVER:
6546 g_value_take_string (value,
6547 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
6549 case PROP_BUNDLE_POLICY:
6550 g_value_set_enum (value, webrtc->bundle_policy);
6552 case PROP_ICE_TRANSPORT_POLICY:
6553 g_value_set_enum (value, webrtc->ice_transport_policy);
6555 case PROP_ICE_AGENT:
6556 g_value_set_object (value, webrtc->priv->ice);
6559 g_value_set_uint (value, webrtc->priv->jb_latency);
6561 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
6563 g_value_set_boolean (value, webrtc->priv->netsim);
6565 case PROP_DROP_PROBABILITY_SENDER:
6566 g_value_set_float (value, webrtc->priv->drop_probability_sender);
6570 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
6577 gst_webrtc_bin_constructed (GObject * object)
6579 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6582 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
6583 webrtc->priv->ice = gst_webrtc_ice_new (name);
6585 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
6586 (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
6592 _free_pending_pad (GstPad * pad)
6594 gst_object_unref (pad);
6598 gst_webrtc_bin_dispose (GObject * object)
6600 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6602 if (webrtc->priv->ice)
6603 gst_object_unref (webrtc->priv->ice);
6604 webrtc->priv->ice = NULL;
6606 if (webrtc->priv->ice_stream_map)
6607 g_array_free (webrtc->priv->ice_stream_map, TRUE);
6608 webrtc->priv->ice_stream_map = NULL;
6610 g_clear_object (&webrtc->priv->sctp_transport);
6612 G_OBJECT_CLASS (parent_class)->dispose (object);
6616 gst_webrtc_bin_finalize (GObject * object)
6618 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6620 if (webrtc->priv->transports)
6621 g_ptr_array_free (webrtc->priv->transports, TRUE);
6622 webrtc->priv->transports = NULL;
6624 if (webrtc->priv->transceivers)
6625 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
6626 webrtc->priv->transceivers = NULL;
6628 if (webrtc->priv->data_channels)
6629 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
6630 webrtc->priv->data_channels = NULL;
6632 if (webrtc->priv->pending_data_channels)
6633 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
6634 webrtc->priv->pending_data_channels = NULL;
6636 if (webrtc->priv->pending_remote_ice_candidates)
6637 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
6638 webrtc->priv->pending_remote_ice_candidates = NULL;
6640 if (webrtc->priv->pending_local_ice_candidates)
6641 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
6642 webrtc->priv->pending_local_ice_candidates = NULL;
6644 if (webrtc->priv->session_mid_map)
6645 g_array_free (webrtc->priv->session_mid_map, TRUE);
6646 webrtc->priv->session_mid_map = NULL;
6648 if (webrtc->priv->pending_pads)
6649 g_list_free_full (webrtc->priv->pending_pads,
6650 (GDestroyNotify) _free_pending_pad);
6651 webrtc->priv->pending_pads = NULL;
6653 if (webrtc->priv->pending_sink_transceivers)
6654 g_list_free_full (webrtc->priv->pending_sink_transceivers,
6655 (GDestroyNotify) gst_object_unref);
6656 webrtc->priv->pending_sink_transceivers = NULL;
6658 if (webrtc->current_local_description)
6659 gst_webrtc_session_description_free (webrtc->current_local_description);
6660 webrtc->current_local_description = NULL;
6661 if (webrtc->pending_local_description)
6662 gst_webrtc_session_description_free (webrtc->pending_local_description);
6663 webrtc->pending_local_description = NULL;
6665 if (webrtc->current_remote_description)
6666 gst_webrtc_session_description_free (webrtc->current_remote_description);
6667 webrtc->current_remote_description = NULL;
6668 if (webrtc->pending_remote_description)
6669 gst_webrtc_session_description_free (webrtc->pending_remote_description);
6670 webrtc->pending_remote_description = NULL;
6672 if (webrtc->priv->last_generated_answer)
6673 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
6674 webrtc->priv->last_generated_answer = NULL;
6675 if (webrtc->priv->last_generated_offer)
6676 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
6677 webrtc->priv->last_generated_offer = NULL;
6679 g_mutex_clear (ICE_GET_LOCK (webrtc));
6680 g_mutex_clear (PC_GET_LOCK (webrtc));
6681 g_cond_clear (PC_GET_COND (webrtc));
6683 G_OBJECT_CLASS (parent_class)->finalize (object);
6687 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
6689 GObjectClass *gobject_class = (GObjectClass *) klass;
6690 GstElementClass *element_class = (GstElementClass *) klass;
6692 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
6693 element_class->release_pad = gst_webrtc_bin_release_pad;
6694 element_class->change_state = gst_webrtc_bin_change_state;
6696 gst_element_class_add_static_pad_template_with_gtype (element_class,
6697 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
6698 gst_element_class_add_static_pad_template (element_class, &src_template);
6700 gst_element_class_set_metadata (element_class, "WebRTC Bin",
6701 "Filter/Network/WebRTC", "A bin for webrtc connections",
6702 "Matthew Waters <matthew@centricular.com>");
6704 gobject_class->constructed = gst_webrtc_bin_constructed;
6705 gobject_class->get_property = gst_webrtc_bin_get_property;
6706 gobject_class->set_property = gst_webrtc_bin_set_property;
6707 gobject_class->dispose = gst_webrtc_bin_dispose;
6708 gobject_class->finalize = gst_webrtc_bin_finalize;
6710 g_object_class_install_property (gobject_class,
6711 PROP_LOCAL_DESCRIPTION,
6712 g_param_spec_boxed ("local-description", "Local Description",
6713 "The local SDP description in use for this connection. "
6714 "Favours a pending description over the current description",
6715 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6716 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6718 g_object_class_install_property (gobject_class,
6719 PROP_CURRENT_LOCAL_DESCRIPTION,
6720 g_param_spec_boxed ("current-local-description",
6721 "Current Local Description",
6722 "The local description that was successfully negotiated the last time "
6723 "the connection transitioned into the stable state",
6724 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6725 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6727 g_object_class_install_property (gobject_class,
6728 PROP_PENDING_LOCAL_DESCRIPTION,
6729 g_param_spec_boxed ("pending-local-description",
6730 "Pending Local Description",
6731 "The local description that is in the process of being negotiated plus "
6732 "any local candidates that have been generated by the ICE Agent since the "
6733 "offer or answer was created",
6734 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6735 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6737 g_object_class_install_property (gobject_class,
6738 PROP_REMOTE_DESCRIPTION,
6739 g_param_spec_boxed ("remote-description", "Remote Description",
6740 "The remote SDP description to use for this connection. "
6741 "Favours a pending description over the current description",
6742 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6743 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6745 g_object_class_install_property (gobject_class,
6746 PROP_CURRENT_REMOTE_DESCRIPTION,
6747 g_param_spec_boxed ("current-remote-description",
6748 "Current Remote Description",
6749 "The last remote description that was successfully negotiated the last "
6750 "time the connection transitioned into the stable state plus any remote "
6751 "candidates that have been supplied via addIceCandidate() since the offer "
6752 "or answer was created",
6753 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6754 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6756 g_object_class_install_property (gobject_class,
6757 PROP_PENDING_REMOTE_DESCRIPTION,
6758 g_param_spec_boxed ("pending-remote-description",
6759 "Pending Remote Description",
6760 "The remote description that is in the process of being negotiated, "
6761 "complete with any remote candidates that have been supplied via "
6762 "addIceCandidate() since the offer or answer was created",
6763 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6764 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6766 g_object_class_install_property (gobject_class,
6768 g_param_spec_string ("stun-server", "STUN Server",
6769 "The STUN server of the form stun://hostname:port",
6770 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6772 g_object_class_install_property (gobject_class,
6774 g_param_spec_string ("turn-server", "TURN Server",
6775 "The TURN server of the form turn(s)://username:password@host:port. "
6776 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
6777 "if you wish to use multiple TURN servers",
6778 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6780 g_object_class_install_property (gobject_class,
6781 PROP_CONNECTION_STATE,
6782 g_param_spec_enum ("connection-state", "Connection State",
6783 "The overall connection state of this element",
6784 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
6785 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
6786 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6788 g_object_class_install_property (gobject_class,
6789 PROP_SIGNALING_STATE,
6790 g_param_spec_enum ("signaling-state", "Signaling State",
6791 "The signaling state of this element",
6792 GST_TYPE_WEBRTC_SIGNALING_STATE,
6793 GST_WEBRTC_SIGNALING_STATE_STABLE,
6794 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6796 g_object_class_install_property (gobject_class,
6797 PROP_ICE_CONNECTION_STATE,
6798 g_param_spec_enum ("ice-connection-state", "ICE connection state",
6799 "The collective connection state of all ICETransport's",
6800 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
6801 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
6802 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6804 g_object_class_install_property (gobject_class,
6805 PROP_ICE_GATHERING_STATE,
6806 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
6807 "The collective gathering state of all ICETransport's",
6808 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
6809 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
6810 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6812 g_object_class_install_property (gobject_class,
6814 g_param_spec_enum ("bundle-policy", "Bundle Policy",
6815 "The policy to apply for bundling",
6816 GST_TYPE_WEBRTC_BUNDLE_POLICY,
6817 GST_WEBRTC_BUNDLE_POLICY_NONE,
6818 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6820 g_object_class_install_property (gobject_class,
6821 PROP_ICE_TRANSPORT_POLICY,
6822 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
6823 "The policy to apply for ICE transport",
6824 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
6825 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
6826 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6828 g_object_class_install_property (gobject_class,
6830 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
6831 "The WebRTC ICE agent",
6832 GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6835 * GstWebRTCBin:latency:
6837 * Default duration to buffer in the jitterbuffers (in ms)
6842 g_object_class_install_property (gobject_class,
6844 g_param_spec_uint ("latency", "Latency",
6845 "Default duration to buffer in the jitterbuffers (in ms)",
6846 0, G_MAXUINT, 200, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6848 #ifdef TIZEN_FEATURE_IMPORT_NETSIM
6849 g_object_class_install_property (gobject_class,
6851 g_param_spec_boolean ("netsim", "Use network simulator",
6852 "Use network simulator for packet loss",
6853 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6855 g_object_class_install_property (gobject_class,
6856 PROP_DROP_PROBABILITY_SENDER,
6857 g_param_spec_float ("drop-probability-sender", "Drop Probability for sender",
6858 "The Probability a sending RTP buffer is dropped",
6860 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
6864 * GstWebRTCBin::create-offer:
6865 * @object: the #webrtcbin
6866 * @options: (nullable): create-offer options
6867 * @promise: a #GstPromise which will contain the offer
6869 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
6870 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
6871 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6872 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
6873 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
6876 * GstWebRTCBin::create-answer:
6877 * @object: the #webrtcbin
6878 * @options: (nullable): create-answer options
6879 * @promise: a #GstPromise which will contain the answer
6881 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
6882 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
6883 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6884 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
6885 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
6888 * GstWebRTCBin::set-local-description:
6889 * @object: the #GstWebRTCBin
6890 * @desc: a #GstWebRTCSessionDescription description
6891 * @promise: (nullable): a #GstPromise to be notified when it's set
6893 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
6894 g_signal_new_class_handler ("set-local-description",
6895 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6896 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
6897 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
6900 * GstWebRTCBin::set-remote-description:
6901 * @object: the #GstWebRTCBin
6902 * @desc: a #GstWebRTCSessionDescription description
6903 * @promise: (nullable): a #GstPromise to be notified when it's set
6905 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
6906 g_signal_new_class_handler ("set-remote-description",
6907 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6908 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
6909 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
6912 * GstWebRTCBin::add-ice-candidate:
6913 * @object: the #webrtcbin
6914 * @mline_index: the index of the media description in the SDP
6915 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
6918 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
6919 g_signal_new_class_handler ("add-ice-candidate",
6920 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6921 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
6922 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
6925 * GstWebRTCBin::get-stats:
6926 * @object: the #webrtcbin
6927 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
6928 * @promise: a #GstPromise for the result
6930 * The @promise will contain the result of retrieving the session statistics.
6931 * The structure will be named 'application/x-webrtc-stats and contain the
6932 * following based on the webrtc-stats spec available from
6933 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
6934 * and is constantly changing these statistics may be changed to fit with
6937 * Each field key is a unique identifier for each RTCStats
6938 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
6939 * GstStructure) in the RTCStatsReport
6940 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
6941 * field in the RTCStats subclass is outlined below.
6943 * Each statistics structure contains the following values as defined by
6944 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
6946 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
6947 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
6948 * "id" G_TYPE_STRING unique identifier
6950 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
6952 * "payload-type" G_TYPE_UINT the rtp payload number in use
6953 * "clock-rate" G_TYPE_UINT the rtp clock-rate
6955 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
6957 * "ssrc" G_TYPE_STRING the rtp sequence src in use
6958 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
6959 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
6960 * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
6961 * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
6962 * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
6964 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
6966 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
6967 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
6968 * "packets-lost" G_TYPE_UINT number of packets lost
6969 * "jitter" G_TYPE_DOUBLE packet jitter measured in secondss
6971 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
6973 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
6975 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
6977 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
6978 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
6980 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
6982 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
6983 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
6985 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
6987 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
6989 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
6991 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
6994 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
6995 g_signal_new_class_handler ("get-stats",
6996 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6997 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
6998 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
7001 * GstWebRTCBin::on-negotiation-needed:
7002 * @object: the #webrtcbin
7004 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
7005 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
7006 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
7009 * GstWebRTCBin::on-ice-candidate:
7010 * @object: the #webrtcbin
7011 * @mline_index: the index of the media description in the SDP
7012 * @candidate: the ICE candidate
7014 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
7015 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
7016 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7017 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7020 * GstWebRTCBin::on-new-transceiver:
7021 * @object: the #webrtcbin
7022 * @candidate: the new #GstWebRTCRTPTransceiver
7024 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
7025 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
7026 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7027 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
7030 * GstWebRTCBin::on-data-channel:
7031 * @object: the #GstWebRTCBin
7032 * @candidate: the new `GstWebRTCDataChannel`
7034 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
7035 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
7036 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7037 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
7040 * GstWebRTCBin::add-transceiver:
7041 * @object: the #webrtcbin
7042 * @direction: the direction of the new transceiver
7043 * @caps: (allow none): the codec preferences for this transceiver
7045 * Returns: the new #GstWebRTCRTPTransceiver
7047 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
7048 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
7049 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7050 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
7051 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
7052 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
7055 * GstWebRTCBin::get-transceivers:
7056 * @object: the #webrtcbin
7058 * Returns: a #GArray of #GstWebRTCRTPTransceivers
7060 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
7061 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
7062 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7063 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
7067 * GstWebRTCBin::get-transceiver:
7068 * @object: the #GstWebRTCBin
7069 * @idx: The index of the transceiver
7071 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
7074 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
7075 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
7076 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7077 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
7078 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
7081 * GstWebRTCBin::add-turn-server:
7082 * @object: the #GstWebRTCBin
7083 * @uri: The uri of the server of the form turn(s)://username:password@host:port
7085 * Add a turn server to obtain ICE candidates from
7087 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
7088 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
7089 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7090 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
7091 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
7094 * GstWebRTCBin::create-data-channel:
7095 * @object: the #GstWebRTCBin
7096 * @label: the label for the data channel
7097 * @options: a #GstStructure of options for creating the data channel
7099 * The options dictionary is the same format as the RTCDataChannelInit
7100 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
7101 * and reproduced below
7103 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
7104 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
7105 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
7106 * protocol G_TYPE_STRING The subprotocol used by this channel
7107 * 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.
7108 * id G_TYPE_INT Override the default identifier selection of this channel
7109 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
7111 * Returns: (transfer full): a new data channel object
7113 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
7114 g_signal_new_class_handler ("create-data-channel",
7115 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7116 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
7117 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
7119 #ifndef TIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK
7120 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
7121 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
7126 _unparent_and_unref (GObject * object)
7128 GstObject *obj = GST_OBJECT (object);
7130 GST_OBJECT_PARENT (obj) = NULL;
7132 gst_object_unref (obj);
7136 _transport_free (GObject * object)
7138 TransportStream *stream = (TransportStream *) object;
7139 GstWebRTCBin *webrtc;
7141 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
7143 if (stream->transport) {
7144 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
7145 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
7148 gst_object_unref (object);
7152 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
7154 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
7155 g_mutex_init (PC_GET_LOCK (webrtc));
7156 g_cond_init (PC_GET_COND (webrtc));
7158 g_mutex_init (ICE_GET_LOCK (webrtc));
7160 webrtc->rtpbin = _create_rtpbin (webrtc);
7161 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
7163 webrtc->priv->transceivers =
7164 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
7165 webrtc->priv->transports =
7166 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
7168 webrtc->priv->data_channels =
7169 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7171 webrtc->priv->pending_data_channels =
7172 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7174 webrtc->priv->session_mid_map =
7175 g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
7176 g_array_set_clear_func (webrtc->priv->session_mid_map,
7177 (GDestroyNotify) clear_session_mid_item);
7179 webrtc->priv->ice_stream_map =
7180 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
7181 webrtc->priv->pending_remote_ice_candidates =
7182 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7183 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
7184 (GDestroyNotify) _clear_ice_candidate_item);
7186 webrtc->priv->pending_local_ice_candidates =
7187 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7188 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
7189 (GDestroyNotify) _clear_ice_candidate_item);
7191 /* we start off closed until we move to READY */
7192 webrtc->priv->is_closed = TRUE;