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 * SECTION: element-webrtcbin
66 * This webrtcbin implements the majority of the W3's peerconnection API and
67 * implementation guide where possible. Generating offers, answers and setting
68 * local and remote SDP's are all supported. Both media descriptions and
69 * descriptions involving data channels are supported.
71 * Each input/output pad is equivalent to a Track in W3 parlance which are
72 * added/removed from the bin. The number of requested sink pads is the number
73 * of streams that will be sent to the receiver and will be associated with a
74 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
76 * On the receiving side, RTPTransceiver's are created in response to setting
77 * a remote description. Output pads for the receiving streams in the set
78 * description are also created when data is received.
80 * A TransportStream is created when needed in order to transport the data over
81 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
82 * on the negotiated SDP's between the peers based on the bundle and rtcp
83 * configuration. Some cases are outlined below for a simple single
84 * audio/video/data session:
86 * - max-bundle uses a single transport for all
87 * media/data transported. Renegotiation involves adding/removing the
88 * necessary streams to the existing transports.
89 * - max-compat involves two TransportStream per media stream
90 * to transport the rtp and the rtcp packets and a single TransportStream for
91 * all data channels. Each stream change involves modifying the associated
92 * TransportStream/s as necessary.
97 * assert sending payload type matches the stream
98 * reconfiguration (of anything)
100 * balanced bundle policy
101 * setting custom DTLS certificates
103 * separate session id's from mlineindex properly
104 * how to deal with replacing a input/output track/stream
107 static void _update_need_negotiation (GstWebRTCBin * webrtc);
109 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
110 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
112 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
115 GST_STATIC_CAPS ("application/x-rtp"));
117 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
120 GST_STATIC_CAPS ("application/x-rtp"));
124 PROP_PAD_TRANSCEIVER = 1,
128 _have_nice_elements (GstWebRTCBin * webrtc)
130 GstPluginFeature *feature;
132 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
134 gst_object_unref (feature);
136 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
137 ("%s", "libnice elements are not available"));
141 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
143 gst_object_unref (feature);
145 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
146 ("%s", "libnice elements are not available"));
154 _have_sctp_elements (GstWebRTCBin * webrtc)
156 GstPluginFeature *feature;
158 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
160 gst_object_unref (feature);
162 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
163 ("%s", "sctp elements are not available"));
167 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
169 gst_object_unref (feature);
171 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
172 ("%s", "sctp elements are not available"));
180 _have_dtls_elements (GstWebRTCBin * webrtc)
182 GstPluginFeature *feature;
184 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
186 gst_object_unref (feature);
188 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
189 ("%s", "dtls elements are not available"));
193 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
195 gst_object_unref (feature);
197 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
198 ("%s", "dtls elements are not available"));
205 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
208 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
209 GValue * value, GParamSpec * pspec)
211 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
214 case PROP_PAD_TRANSCEIVER:
215 g_value_set_object (value, pad->trans);
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 gst_webrtc_bin_pad_finalize (GObject * object)
226 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
229 gst_object_unref (pad->trans);
232 if (pad->received_caps)
233 gst_caps_unref (pad->received_caps);
234 pad->received_caps = NULL;
236 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
240 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
242 GObjectClass *gobject_class = (GObjectClass *) klass;
244 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
245 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
247 g_object_class_install_property (gobject_class,
248 PROP_PAD_TRANSCEIVER,
249 g_param_spec_object ("transceiver", "Transceiver",
250 "Transceiver associated with this pad",
251 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
252 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
256 gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
258 if (wpad->received_caps) {
259 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
260 GstPad *pad = GST_PAD (wpad);
263 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
264 gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
265 trans->current_ssrc, NULL));
266 gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
271 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
273 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
274 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
275 gboolean check_negotiation = FALSE;
277 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
280 gst_event_parse_caps (event, &caps);
281 check_negotiation = (!wpad->received_caps
282 || gst_caps_is_equal (wpad->received_caps, caps));
283 gst_caps_replace (&wpad->received_caps, caps);
285 GST_DEBUG_OBJECT (parent,
286 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
287 GST_PTR_FORMAT, pad, check_negotiation, caps);
289 if (check_negotiation) {
290 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
291 const GstStructure *s;
293 s = gst_caps_get_structure (caps, 0);
294 gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
295 gst_webrtc_bin_pad_update_ssrc_event (wpad);
297 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
298 check_negotiation = TRUE;
301 if (check_negotiation) {
303 _update_need_negotiation (webrtc);
307 return gst_pad_event_default (pad, parent, event);
311 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
315 static GstWebRTCBinPad *
316 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
318 GstWebRTCBinPad *pad;
319 GstPadTemplate *template;
321 if (direction == GST_PAD_SINK)
322 template = gst_static_pad_template_get (&sink_template);
323 else if (direction == GST_PAD_SRC)
324 template = gst_static_pad_template_get (&src_template);
326 g_assert_not_reached ();
329 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
330 direction, "template", template, NULL);
331 gst_object_unref (template);
333 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
335 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
336 direction == GST_PAD_SRC ? "src" : "sink");
340 #define gst_webrtc_bin_parent_class parent_class
341 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
342 G_ADD_PRIVATE (GstWebRTCBin)
343 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
344 "webrtcbin element"););
346 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
347 GstWebRTCBinPad * pad);
353 CREATE_ANSWER_SIGNAL,
354 SET_LOCAL_DESCRIPTION_SIGNAL,
355 SET_REMOTE_DESCRIPTION_SIGNAL,
356 ADD_ICE_CANDIDATE_SIGNAL,
357 ON_NEGOTIATION_NEEDED_SIGNAL,
358 ON_ICE_CANDIDATE_SIGNAL,
359 ON_NEW_TRANSCEIVER_SIGNAL,
361 ADD_TRANSCEIVER_SIGNAL,
362 GET_TRANSCEIVER_SIGNAL,
363 GET_TRANSCEIVERS_SIGNAL,
364 ADD_TURN_SERVER_SIGNAL,
365 CREATE_DATA_CHANNEL_SIGNAL,
366 ON_DATA_CHANNEL_SIGNAL,
373 PROP_CONNECTION_STATE,
374 PROP_SIGNALING_STATE,
375 PROP_ICE_GATHERING_STATE,
376 PROP_ICE_CONNECTION_STATE,
377 PROP_LOCAL_DESCRIPTION,
378 PROP_CURRENT_LOCAL_DESCRIPTION,
379 PROP_PENDING_LOCAL_DESCRIPTION,
380 PROP_REMOTE_DESCRIPTION,
381 PROP_CURRENT_REMOTE_DESCRIPTION,
382 PROP_PENDING_REMOTE_DESCRIPTION,
386 PROP_ICE_TRANSPORT_POLICY,
391 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
396 GstWebRTCICEStream *stream;
399 /* FIXME: locking? */
401 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
405 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
406 IceStreamItem *item =
407 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
409 if (item->session_id == session_id) {
410 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
411 "session %u", item->stream, session_id);
416 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
422 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
423 GstWebRTCICEStream * stream)
425 IceStreamItem item = { session_id, stream };
427 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
428 "session %u", stream, session_id);
429 g_array_append_val (webrtc->priv->ice_stream_map, item);
432 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
435 static GstWebRTCRTPTransceiver *
436 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
437 FindTransceiverFunc func)
441 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
442 GstWebRTCRTPTransceiver *transceiver =
443 g_ptr_array_index (webrtc->priv->transceivers, i);
445 if (func (transceiver, data))
453 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
455 return g_strcmp0 (trans->mid, mid) == 0;
459 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
464 return trans->mline == *mline;
467 static GstWebRTCRTPTransceiver *
468 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
470 GstWebRTCRTPTransceiver *trans;
472 trans = _find_transceiver (webrtc, &mlineindex,
473 (FindTransceiverFunc) transceiver_match_for_mline);
475 GST_TRACE_OBJECT (webrtc,
476 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
482 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
485 static TransportStream *
486 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
487 FindTransportFunc func)
491 for (i = 0; i < webrtc->priv->transports->len; i++) {
492 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
494 if (func (stream, data))
502 match_stream_for_session (TransportStream * trans, guint * session)
504 return trans->session_id == *session;
507 static TransportStream *
508 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
510 TransportStream *stream;
512 stream = _find_transport (webrtc, &session_id,
513 (FindTransportFunc) match_stream_for_session);
515 GST_TRACE_OBJECT (webrtc,
516 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
521 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
523 static GstWebRTCBinPad *
524 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
526 GstElement *element = GST_ELEMENT (webrtc);
529 GST_OBJECT_LOCK (webrtc);
531 for (; l; l = g_list_next (l)) {
532 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
534 if (func (l->data, data)) {
535 gst_object_ref (l->data);
536 GST_OBJECT_UNLOCK (webrtc);
541 l = webrtc->priv->pending_pads;
542 for (; l; l = g_list_next (l)) {
543 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
545 if (func (l->data, data)) {
546 gst_object_ref (l->data);
547 GST_OBJECT_UNLOCK (webrtc);
551 GST_OBJECT_UNLOCK (webrtc);
556 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
559 static WebRTCDataChannel *
560 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
561 FindDataChannelFunc func)
565 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
566 WebRTCDataChannel *channel =
567 g_ptr_array_index (webrtc->priv->data_channels, i);
569 if (func (channel, data))
577 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
579 return channel->parent.id == *id;
582 static WebRTCDataChannel *
583 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
585 WebRTCDataChannel *channel;
587 channel = _find_data_channel (webrtc, &id,
588 (FindDataChannelFunc) data_channel_match_for_id);
590 GST_TRACE_OBJECT (webrtc,
591 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
597 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
599 GST_OBJECT_LOCK (webrtc);
600 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
601 GST_OBJECT_UNLOCK (webrtc);
605 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
607 GST_OBJECT_LOCK (webrtc);
608 webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
609 GST_OBJECT_UNLOCK (webrtc);
613 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
615 _remove_pending_pad (webrtc, pad);
617 if (webrtc->priv->running)
618 gst_pad_set_active (GST_PAD (pad), TRUE);
619 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
623 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
625 _remove_pending_pad (webrtc, pad);
627 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
632 GstPadDirection direction;
637 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
639 return GST_PAD_DIRECTION (pad) == match->direction
640 && pad->trans->mline == match->mline;
643 static GstWebRTCBinPad *
644 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
647 MLineMatch m = { direction, mline };
649 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
654 GstPadDirection direction;
655 GstWebRTCRTPTransceiver *trans;
659 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
661 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
664 static GstWebRTCBinPad *
665 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
666 GstWebRTCRTPTransceiver * trans)
668 TransMatch m = { direction, trans };
670 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
675 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
677 return pad->ssrc == *ssrc;
681 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
688 _unlock_pc_thread (GMutex * lock)
690 g_mutex_unlock (lock);
691 return G_SOURCE_REMOVE;
695 _gst_pc_thread (GstWebRTCBin * webrtc)
698 webrtc->priv->main_context = g_main_context_new ();
699 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
701 PC_COND_BROADCAST (webrtc);
702 g_main_context_invoke (webrtc->priv->main_context,
703 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
705 /* Having the thread be the thread default GMainContext will break the
706 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
708 g_main_loop_run (webrtc->priv->loop);
710 GST_OBJECT_LOCK (webrtc);
711 g_main_context_unref (webrtc->priv->main_context);
712 webrtc->priv->main_context = NULL;
713 GST_OBJECT_UNLOCK (webrtc);
716 g_main_loop_unref (webrtc->priv->loop);
717 webrtc->priv->loop = NULL;
718 PC_COND_BROADCAST (webrtc);
725 _start_thread (GstWebRTCBin * webrtc)
730 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
731 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
735 while (!webrtc->priv->loop)
736 PC_COND_WAIT (webrtc);
737 webrtc->priv->is_closed = FALSE;
742 _stop_thread (GstWebRTCBin * webrtc)
744 GST_OBJECT_LOCK (webrtc);
745 webrtc->priv->is_closed = TRUE;
746 GST_OBJECT_UNLOCK (webrtc);
749 g_main_loop_quit (webrtc->priv->loop);
750 while (webrtc->priv->loop)
751 PC_COND_WAIT (webrtc);
754 g_thread_unref (webrtc->priv->thread);
758 _execute_op (GstWebRTCBinTask * op)
760 PC_LOCK (op->webrtc);
761 if (op->webrtc->priv->is_closed) {
762 PC_UNLOCK (op->webrtc);
766 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
767 "webrtcbin is closed. aborting execution.");
769 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
770 "error", G_TYPE_ERROR, error, NULL);
772 gst_promise_reply (op->promise, s);
774 g_clear_error (&error);
776 GST_DEBUG_OBJECT (op->webrtc,
777 "Peerconnection is closed, aborting execution");
781 op->op (op->webrtc, op->data);
783 PC_UNLOCK (op->webrtc);
786 return G_SOURCE_REMOVE;
790 _free_op (GstWebRTCBinTask * op)
793 op->notify (op->data);
795 gst_promise_unref (op->promise);
800 * @promise is for correctly signalling the failure case to the caller when
801 * the user supplies it. Without passing it in, the promise would never
802 * be replied to in the case that @webrtc becomes closed between the idle
803 * source addition and the the execution of the idle source.
806 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
807 gpointer data, GDestroyNotify notify, GstPromise * promise)
809 GstWebRTCBinTask *op;
813 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
815 GST_OBJECT_LOCK (webrtc);
816 if (webrtc->priv->is_closed) {
817 GST_OBJECT_UNLOCK (webrtc);
818 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
823 ctx = g_main_context_ref (webrtc->priv->main_context);
824 GST_OBJECT_UNLOCK (webrtc);
826 op = g_new0 (GstWebRTCBinTask, 1);
832 op->promise = gst_promise_ref (promise);
834 source = g_idle_source_new ();
835 g_source_set_priority (source, G_PRIORITY_DEFAULT);
836 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
837 (GDestroyNotify) _free_op);
838 g_source_attach (source, ctx);
839 g_source_unref (source);
840 g_main_context_unref (ctx);
845 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
846 static GstWebRTCICEConnectionState
847 _collate_ice_connection_states (GstWebRTCBin * webrtc)
849 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
850 GstWebRTCICEConnectionState any_state = 0;
851 gboolean all_new_or_closed = TRUE;
852 gboolean all_completed_or_closed = TRUE;
853 gboolean all_connected_completed_or_closed = TRUE;
856 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
857 GstWebRTCRTPTransceiver *rtp_trans =
858 g_ptr_array_index (webrtc->priv->transceivers, i);
859 GstWebRTCICETransport *transport;
860 GstWebRTCICEConnectionState ice_state;
862 if (rtp_trans->stopped) {
863 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
867 if (!rtp_trans->mid) {
868 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
872 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
874 /* get transport state */
875 g_object_get (transport, "state", &ice_state, NULL);
876 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
878 any_state |= (1 << ice_state);
880 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
881 all_new_or_closed = FALSE;
882 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
883 all_completed_or_closed = FALSE;
884 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
885 && ice_state != STATE (CLOSED))
886 all_connected_completed_or_closed = FALSE;
889 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
891 if (webrtc->priv->is_closed) {
892 GST_TRACE_OBJECT (webrtc, "returning closed");
893 return STATE (CLOSED);
895 /* Any of the RTCIceTransports are in the failed state. */
896 if (any_state & (1 << STATE (FAILED))) {
897 GST_TRACE_OBJECT (webrtc, "returning failed");
898 return STATE (FAILED);
900 /* Any of the RTCIceTransports are in the disconnected state. */
901 if (any_state & (1 << STATE (DISCONNECTED))) {
902 GST_TRACE_OBJECT (webrtc, "returning disconnected");
903 return STATE (DISCONNECTED);
905 /* All of the RTCIceTransports are in the new or closed state, or there are
907 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
908 GST_TRACE_OBJECT (webrtc, "returning new");
911 /* Any of the RTCIceTransports are in the checking or new state. */
912 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
913 GST_TRACE_OBJECT (webrtc, "returning checking");
914 return STATE (CHECKING);
916 /* All RTCIceTransports are in the completed or closed state. */
917 if (all_completed_or_closed) {
918 GST_TRACE_OBJECT (webrtc, "returning completed");
919 return STATE (COMPLETED);
921 /* All RTCIceTransports are in the connected, completed or closed state. */
922 if (all_connected_completed_or_closed) {
923 GST_TRACE_OBJECT (webrtc, "returning connected");
924 return STATE (CONNECTED);
927 GST_FIXME ("unspecified situation, returning old state");
928 return webrtc->ice_connection_state;
932 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
933 static GstWebRTCICEGatheringState
934 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
936 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
937 GstWebRTCICEGatheringState any_state = 0;
938 gboolean all_completed = webrtc->priv->transceivers->len > 0;
941 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
942 GstWebRTCRTPTransceiver *rtp_trans =
943 g_ptr_array_index (webrtc->priv->transceivers, i);
944 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
945 TransportStream *stream = trans->stream;
946 GstWebRTCDTLSTransport *dtls_transport;
947 GstWebRTCICETransport *transport;
948 GstWebRTCICEGatheringState ice_state;
950 if (rtp_trans->stopped || stream == NULL) {
951 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
956 /* We only have a mid in the transceiver after we got the SDP answer,
957 * which is usually long after gathering has finished */
958 if (!rtp_trans->mid) {
959 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
962 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
963 if (dtls_transport == NULL) {
964 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
968 transport = dtls_transport->transport;
970 /* get gathering state */
971 g_object_get (transport, "gathering-state", &ice_state, NULL);
972 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
974 any_state |= (1 << ice_state);
975 if (ice_state != STATE (COMPLETE))
976 all_completed = FALSE;
979 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
981 /* Any of the RTCIceTransport s are in the gathering state. */
982 if (any_state & (1 << STATE (GATHERING))) {
983 GST_TRACE_OBJECT (webrtc, "returning gathering");
984 return STATE (GATHERING);
986 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
987 * the completed gathering state. */
989 GST_TRACE_OBJECT (webrtc, "returning complete");
990 return STATE (COMPLETE);
993 /* Any of the RTCIceTransport s are in the new gathering state and none
994 * of the transports are in the gathering state, or there are no transports. */
995 GST_TRACE_OBJECT (webrtc, "returning new");
1000 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1001 static GstWebRTCPeerConnectionState
1002 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1004 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1005 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1006 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1007 GstWebRTCICEConnectionState any_ice_state = 0;
1008 GstWebRTCDTLSTransportState any_dtls_state = 0;
1009 gboolean ice_all_new_or_closed = TRUE;
1010 gboolean dtls_all_new_or_closed = TRUE;
1011 gboolean ice_all_new_connecting_or_checking = TRUE;
1012 gboolean dtls_all_new_connecting_or_checking = TRUE;
1013 gboolean ice_all_connected_completed_or_closed = TRUE;
1014 gboolean dtls_all_connected_completed_or_closed = TRUE;
1017 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1018 GstWebRTCRTPTransceiver *rtp_trans =
1019 g_ptr_array_index (webrtc->priv->transceivers, i);
1020 GstWebRTCDTLSTransport *transport;
1021 GstWebRTCICEConnectionState ice_state;
1022 GstWebRTCDTLSTransportState dtls_state;
1024 if (rtp_trans->stopped) {
1025 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1028 if (!rtp_trans->mid) {
1029 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1033 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1035 /* get transport state */
1036 g_object_get (transport, "state", &dtls_state, NULL);
1037 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1039 any_dtls_state |= (1 << dtls_state);
1041 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1042 dtls_all_new_or_closed = FALSE;
1043 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1044 dtls_all_new_connecting_or_checking = FALSE;
1045 if (dtls_state != DTLS_STATE (CONNECTED)
1046 && dtls_state != DTLS_STATE (CLOSED))
1047 dtls_all_connected_completed_or_closed = FALSE;
1049 g_object_get (transport->transport, "state", &ice_state, NULL);
1050 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1052 any_ice_state |= (1 << ice_state);
1054 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1055 ice_all_new_or_closed = FALSE;
1056 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1057 ice_all_new_connecting_or_checking = FALSE;
1058 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1059 && ice_state != ICE_STATE (CLOSED))
1060 ice_all_connected_completed_or_closed = FALSE;
1063 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1064 "state: 0x%x", any_ice_state, any_dtls_state);
1066 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1067 if (webrtc->priv->is_closed) {
1068 GST_TRACE_OBJECT (webrtc, "returning closed");
1069 return STATE (CLOSED);
1072 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1073 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1074 GST_TRACE_OBJECT (webrtc, "returning failed");
1075 return STATE (FAILED);
1077 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1078 GST_TRACE_OBJECT (webrtc, "returning failed");
1079 return STATE (FAILED);
1082 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1084 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1085 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1086 return STATE (DISCONNECTED);
1089 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1090 * state, or there are no transports. */
1091 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1092 || webrtc->priv->transceivers->len == 0) {
1093 GST_TRACE_OBJECT (webrtc, "returning new");
1097 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1098 * or checking state. */
1099 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1100 GST_TRACE_OBJECT (webrtc, "returning connecting");
1101 return STATE (CONNECTING);
1104 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1105 * completed or closed state. */
1106 if (dtls_all_connected_completed_or_closed
1107 && ice_all_connected_completed_or_closed) {
1108 GST_TRACE_OBJECT (webrtc, "returning connected");
1109 return STATE (CONNECTED);
1112 /* FIXME: Unspecified state that happens for us */
1113 if ((dtls_all_new_connecting_or_checking
1114 || dtls_all_connected_completed_or_closed)
1115 && (ice_all_new_connecting_or_checking
1116 || ice_all_connected_completed_or_closed)) {
1117 GST_TRACE_OBJECT (webrtc, "returning connecting");
1118 return STATE (CONNECTING);
1121 GST_FIXME_OBJECT (webrtc,
1122 "Undefined situation detected, returning old state");
1123 return webrtc->peer_connection_state;
1130 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1132 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1133 GstWebRTCICEGatheringState new_state;
1135 new_state = _collate_ice_gathering_states (webrtc);
1137 /* If the new state is complete, before we update the public state,
1138 * check if anyone published more ICE candidates while we were collating
1139 * and stop if so, because it means there's a new later
1140 * ice_gathering_state_task queued */
1141 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1143 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1144 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1145 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1147 ICE_UNLOCK (webrtc);
1150 if (new_state != webrtc->ice_gathering_state) {
1151 gchar *old_s, *new_s;
1153 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1155 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1157 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1158 old_s, old_state, new_s, new_state);
1162 webrtc->ice_gathering_state = new_state;
1164 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1170 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1172 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1177 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1179 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1180 GstWebRTCICEConnectionState new_state;
1182 new_state = _collate_ice_connection_states (webrtc);
1184 if (new_state != old_state) {
1185 gchar *old_s, *new_s;
1187 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1189 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1191 GST_INFO_OBJECT (webrtc,
1192 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1197 webrtc->ice_connection_state = new_state;
1199 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1205 _update_ice_connection_state (GstWebRTCBin * webrtc)
1207 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1212 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1214 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1215 GstWebRTCPeerConnectionState new_state;
1217 new_state = _collate_peer_connection_states (webrtc);
1219 if (new_state != old_state) {
1220 gchar *old_s, *new_s;
1222 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1224 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1226 GST_INFO_OBJECT (webrtc,
1227 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1232 webrtc->peer_connection_state = new_state;
1234 g_object_notify (G_OBJECT (webrtc), "connection-state");
1240 _update_peer_connection_state (GstWebRTCBin * webrtc)
1242 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1247 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1250 gboolean res = FALSE;
1252 GST_OBJECT_LOCK (webrtc);
1253 l = GST_ELEMENT (webrtc)->pads;
1254 for (; l; l = g_list_next (l)) {
1255 GstWebRTCBinPad *wpad;
1257 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1260 wpad = GST_WEBRTC_BIN_PAD (l->data);
1261 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1262 && (!wpad->trans || !wpad->trans->stopped)) {
1267 l = webrtc->priv->pending_pads;
1268 for (; l; l = g_list_next (l)) {
1269 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1277 GST_OBJECT_UNLOCK (webrtc);
1281 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1283 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1287 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1289 /* We can't negotiate until we have received caps on all our sink pads,
1290 * as we will need the ssrcs in our offer / answer */
1291 if (!_all_sinks_have_caps (webrtc)) {
1292 GST_LOG_OBJECT (webrtc,
1293 "no negotiation possible until caps have been received on all sink pads");
1297 /* If any implementation-specific negotiation is required, as described at
1298 * the start of this section, return "true".
1300 /* FIXME: emit when input caps/format changes? */
1302 if (!webrtc->current_local_description) {
1303 GST_LOG_OBJECT (webrtc, "no local description set");
1307 if (!webrtc->current_remote_description) {
1308 GST_LOG_OBJECT (webrtc, "no remote description set");
1312 /* If connection has created any RTCDataChannel's, and no m= section has
1313 * been negotiated yet for data, return "true". */
1314 if (webrtc->priv->data_channels->len > 0) {
1315 if (_message_get_datachannel_index (webrtc->current_local_description->
1316 sdp) >= G_MAXUINT) {
1317 GST_LOG_OBJECT (webrtc,
1318 "no data channel media section and have %u " "transports",
1319 webrtc->priv->data_channels->len);
1324 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1325 GstWebRTCRTPTransceiver *trans;
1327 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1329 if (trans->stopped) {
1330 /* FIXME: If t is stopped and is associated with an m= section according to
1331 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1332 * rejected in connection's currentLocalDescription or
1333 * currentRemoteDescription , return "true". */
1334 GST_FIXME_OBJECT (webrtc,
1335 "check if the transceiver is rejected in descriptions");
1337 const GstSDPMedia *media;
1338 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1340 if (trans->mline == -1 || trans->mid == NULL) {
1341 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1342 " mid %s", i, trans, trans->mid);
1345 /* internal inconsistency */
1346 g_assert (trans->mline <
1347 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1348 g_assert (trans->mline <
1349 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1351 /* FIXME: msid handling
1352 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1353 * section in connection's currentLocalDescription doesn't contain an
1354 * "a=msid" line, return "true". */
1357 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1359 local_dir = _get_direction_from_media (media);
1362 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1364 remote_dir = _get_direction_from_media (media);
1366 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1367 /* If connection's currentLocalDescription if of type "offer", and
1368 * the direction of the associated m= section in neither the offer
1369 * nor answer matches t's direction, return "true". */
1371 if (local_dir != trans->direction && remote_dir != trans->direction) {
1372 gchar *local_str, *remote_str, *dir_str;
1375 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1378 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1381 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1384 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1385 "description (local %s remote %s)", dir_str, local_str,
1390 g_free (remote_str);
1394 } else if (webrtc->current_local_description->type ==
1395 GST_WEBRTC_SDP_TYPE_ANSWER) {
1396 GstWebRTCRTPTransceiverDirection intersect_dir;
1398 /* If connection's currentLocalDescription if of type "answer", and
1399 * the direction of the associated m= section in the answer does not
1400 * match t's direction intersected with the offered direction (as
1401 * described in [JSEP] (section 5.3.1.)), return "true". */
1403 /* remote is the offer, local is the answer */
1404 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1406 if (intersect_dir != trans->direction) {
1407 gchar *local_str, *remote_str, *inter_str, *dir_str;
1410 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1413 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1416 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1419 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1422 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1423 "description intersected direction %s (local %s remote %s)",
1424 dir_str, local_str, inter_str, remote_str);
1428 g_free (remote_str);
1437 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1442 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1444 if (webrtc->priv->need_negotiation) {
1445 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1447 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1453 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1455 _update_need_negotiation (GstWebRTCBin * webrtc)
1457 /* If connection's [[isClosed]] slot is true, abort these steps. */
1458 if (webrtc->priv->is_closed)
1460 /* If connection's signaling state is not "stable", abort these steps. */
1461 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1464 /* If the result of checking if negotiation is needed is "false", clear the
1465 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1466 * to false, and abort these steps. */
1467 if (!_check_if_negotiation_is_needed (webrtc)) {
1468 webrtc->priv->need_negotiation = FALSE;
1471 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1472 if (webrtc->priv->need_negotiation)
1474 /* Set connection's [[needNegotiation]] slot to true. */
1475 webrtc->priv->need_negotiation = TRUE;
1476 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1477 * true, fire a simple event named negotiationneeded at connection. */
1478 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1483 _find_codec_preferences (GstWebRTCBin * webrtc,
1484 GstWebRTCRTPTransceiver * rtp_trans, GstPadDirection direction,
1487 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1488 GstCaps *ret = NULL;
1490 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1493 if (rtp_trans && rtp_trans->codec_preferences) {
1494 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1495 rtp_trans->codec_preferences);
1496 ret = gst_caps_ref (rtp_trans->codec_preferences);
1498 GstWebRTCBinPad *pad = NULL;
1500 /* try to find a pad */
1502 || !(pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans)))
1503 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1506 if (trans && trans->last_configured_caps)
1507 ret = gst_caps_ref (trans->last_configured_caps);
1509 GstCaps *caps = NULL;
1511 if (pad->received_caps) {
1512 caps = gst_caps_ref (pad->received_caps);
1513 } else if ((caps = gst_pad_get_current_caps (GST_PAD (pad)))) {
1514 GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1517 static GstStaticCaps static_filter =
1518 GST_STATIC_CAPS ("application/x-rtp, "
1519 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1520 GstCaps *filter = gst_static_caps_get (&static_filter);
1522 filter = gst_caps_make_writable (filter);
1524 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
1525 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
1526 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
1527 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
1529 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1530 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1533 if (!gst_caps_is_fixed (caps) || gst_caps_is_equal_fixed (caps, filter)
1534 || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
1535 gst_caps_unref (caps);
1538 gst_caps_unref (filter);
1542 gst_caps_replace (&trans->last_configured_caps, caps);
1547 gst_object_unref (pad);
1552 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1558 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1559 WebRTCTransceiver * trans, const GstCaps * caps)
1567 ret = gst_caps_make_writable (caps);
1569 for (i = 0; i < gst_caps_get_size (ret); i++) {
1570 GstStructure *s = gst_caps_get_structure (ret, i);
1573 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1574 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1576 if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1577 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1578 /* FIXME: is this needed? */
1579 /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1580 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1582 /* FIXME: codec-specific parameters? */
1589 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1590 GParamSpec * pspec, GstWebRTCBin * webrtc)
1592 _update_ice_connection_state (webrtc);
1593 _update_peer_connection_state (webrtc);
1597 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1598 GParamSpec * pspec, GstWebRTCBin * webrtc)
1600 _update_ice_gathering_state (webrtc);
1604 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1605 GParamSpec * pspec, GstWebRTCBin * webrtc)
1607 _update_peer_connection_state (webrtc);
1611 match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
1613 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1615 return (trans->current_ssrc == GPOINTER_TO_UINT (data));
1619 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
1620 gboolean early, gpointer user_data)
1622 GstWebRTCBin *webrtc = user_data;
1623 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
1624 GstRTCPPacket packet;
1626 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
1629 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
1630 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
1632 GstWebRTCRTPTransceiver *rtp_trans;
1633 WebRTCTransceiver *trans;
1635 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
1638 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1640 trans = (WebRTCTransceiver *) rtp_trans;
1642 if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
1644 gchar *pad_name = NULL;
1647 g_strdup_printf ("send_rtcp_src_%u",
1648 rtp_trans->sender->transport->session_id);
1649 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
1652 gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
1653 gst_object_unref (pad);
1659 gst_rtcp_buffer_unmap (&rtcp);
1662 /* False means we don't care about suppression */
1667 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
1669 GObject *internal_session = NULL;
1671 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
1672 session_id, &internal_session);
1674 if (internal_session) {
1675 g_signal_connect (internal_session, "on-sending-rtcp",
1676 G_CALLBACK (_on_sending_rtcp), webrtc);
1677 g_object_unref (internal_session);
1681 static GstPadProbeReturn
1682 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1684 GstWebRTCBin *webrtc = user_data;
1686 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
1687 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
1688 const GstStructure *s =
1689 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
1691 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
1695 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1696 GstWebRTCRTPTransceiver *rtp_trans;
1698 rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1701 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1702 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
1703 trans->stream->session_id);
1706 /* Set DSCP field based on
1707 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
1709 switch (rtp_trans->sender->priority) {
1710 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
1713 case GST_WEBRTC_PRIORITY_TYPE_LOW:
1716 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
1717 switch (rtp_trans->kind) {
1718 case GST_WEBRTC_KIND_AUDIO:
1721 case GST_WEBRTC_KIND_VIDEO:
1722 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
1724 case GST_WEBRTC_KIND_UNKNOWN:
1729 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
1730 switch (rtp_trans->kind) {
1731 case GST_WEBRTC_KIND_AUDIO:
1734 case GST_WEBRTC_KIND_VIDEO:
1735 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
1737 case GST_WEBRTC_KIND_UNKNOWN:
1744 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
1746 } else if (gst_structure_get_enum (s, "sctp-priority",
1747 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
1750 /* Set DSCP field based on
1751 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
1754 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
1757 case GST_WEBRTC_PRIORITY_TYPE_LOW:
1760 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
1761 dscp = 10; /* AF11 */
1763 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
1764 dscp = 18; /* AF21 */
1767 if (webrtc->priv->data_channel_transport)
1768 gst_webrtc_ice_set_tos (webrtc->priv->ice,
1769 webrtc->priv->data_channel_transport->stream, dscp << 2);
1773 return GST_PAD_PROBE_OK;
1776 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
1779 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
1781 GstWebRTCPriorityType sctp_priority = 0;
1784 if (!webrtc->priv->sctp_transport)
1787 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1788 GstWebRTCDataChannel *channel
1789 = g_ptr_array_index (webrtc->priv->data_channels, i);
1791 sctp_priority = MAX (sctp_priority, channel->priority);
1794 /* Default priority is low means DSCP field is left as 0 */
1795 if (sctp_priority == 0)
1796 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
1798 /* Nobody asks for DSCP, leave it as-is */
1799 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
1800 !webrtc->priv->tos_attached)
1803 /* If one stream has a non-default priority, then everyone else does too */
1804 gst_webrtc_bin_attach_tos (webrtc);
1806 gst_webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
1811 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
1812 GstWebRTCICETransport * transport)
1816 pad = gst_element_get_static_pad (transport->sink, "sink");
1817 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1818 _nicesink_pad_probe, g_object_ref (webrtc),
1819 (GDestroyNotify) gst_object_unref);
1820 gst_object_unref (pad);
1824 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
1828 if (webrtc->priv->tos_attached)
1830 webrtc->priv->tos_attached = TRUE;
1832 for (i = 0; i < webrtc->priv->transports->len; i++) {
1833 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
1835 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
1837 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
1838 stream->transport->transport);
1841 gst_webrtc_bin_update_sctp_priority (webrtc);
1844 static WebRTCTransceiver *
1845 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
1846 GstWebRTCRTPTransceiverDirection direction, guint mline)
1848 WebRTCTransceiver *trans;
1849 GstWebRTCRTPTransceiver *rtp_trans;
1850 GstWebRTCRTPSender *sender;
1851 GstWebRTCRTPReceiver *receiver;
1853 sender = gst_webrtc_rtp_sender_new ();
1854 receiver = gst_webrtc_rtp_receiver_new ();
1855 trans = webrtc_transceiver_new (webrtc, sender, receiver);
1856 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1857 rtp_trans->direction = direction;
1858 rtp_trans->mline = mline;
1859 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
1860 rtp_trans->stopped = FALSE;
1862 g_signal_connect_object (sender, "notify::priority",
1863 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
1865 g_ptr_array_add (webrtc->priv->transceivers, trans);
1867 gst_object_unref (sender);
1868 gst_object_unref (receiver);
1870 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
1876 static TransportStream *
1877 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1879 GstWebRTCDTLSTransport *transport;
1880 TransportStream *ret;
1882 /* FIXME: how to parametrize the sender and the receiver */
1883 ret = transport_stream_new (webrtc, session_id);
1884 transport = ret->transport;
1886 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1887 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1888 g_signal_connect (G_OBJECT (transport->transport),
1889 "notify::gathering-state",
1890 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1891 g_signal_connect (G_OBJECT (transport), "notify::state",
1892 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1893 if (webrtc->priv->tos_attached)
1894 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
1896 GST_TRACE_OBJECT (webrtc,
1897 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1902 static TransportStream *
1903 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1905 TransportStream *ret;
1908 ret = _find_transport_for_session (webrtc, session_id);
1911 ret = _create_transport_channel (webrtc, session_id);
1912 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1913 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1914 g_ptr_array_add (webrtc->priv->transports, ret);
1916 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1917 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1918 GST_ELEMENT (webrtc->rtpbin), pad_name))
1919 g_warn_if_reached ();
1922 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1923 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1924 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1925 g_warn_if_reached ();
1926 if (webrtc->priv->tos_attached)
1927 gst_webrtc_bin_attach_tos_to_session (webrtc, ret->session_id);
1931 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1932 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1937 /* this is called from the webrtc thread with the pc lock held */
1939 _on_data_channel_ready_state (WebRTCDataChannel * channel,
1940 GParamSpec * pspec, GstWebRTCBin * webrtc)
1942 GstWebRTCDataChannelState ready_state;
1945 g_object_get (channel, "ready-state", &ready_state, NULL);
1947 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
1948 gboolean found = FALSE;
1950 for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) {
1951 WebRTCDataChannel *c;
1953 c = g_ptr_array_index (webrtc->priv->pending_data_channels, i);
1956 g_ptr_array_remove_index (webrtc->priv->pending_data_channels, i);
1960 if (found == FALSE) {
1961 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
1965 g_ptr_array_add (webrtc->priv->data_channels, channel);
1967 gst_webrtc_bin_update_sctp_priority (webrtc);
1969 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
1970 gst_object_ref (channel));
1975 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
1976 GstWebRTCBin * webrtc)
1978 WebRTCDataChannel *channel;
1982 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
1986 channel = _find_data_channel_for_id (webrtc, stream_id);
1988 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
1989 channel->parent.id = stream_id;
1990 channel->webrtcbin = webrtc;
1992 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
1993 gst_bin_add (GST_BIN (webrtc), channel->appsink);
1995 gst_element_sync_state_with_parent (channel->appsrc);
1996 gst_element_sync_state_with_parent (channel->appsink);
1998 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2000 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2003 g_signal_connect (channel, "notify::ready-state",
2004 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2006 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2007 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2008 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2009 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2010 gst_object_unref (sink_pad);
2015 _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
2016 GstWebRTCBin * webrtc)
2018 GstWebRTCSCTPTransportState state;
2020 g_object_get (sctp, "state", &state, NULL);
2022 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2026 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2028 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2029 WebRTCDataChannel *channel;
2031 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2033 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2035 if (!channel->parent.negotiated && !channel->opened)
2036 webrtc_data_channel_start_negotiation (channel);
2042 /* Forward declaration so we can easily disconnect the signal handler */
2043 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2044 GParamSpec * pspec, GstWebRTCBin * webrtc);
2047 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2049 TransportStream *stream;
2050 GstWebRTCDTLSTransport *transport;
2051 GstWebRTCDTLSTransportState dtls_state;
2052 GstWebRTCSCTPTransport *sctp_transport;
2054 stream = webrtc->priv->data_channel_transport;
2055 transport = stream->transport;
2057 g_object_get (transport, "state", &dtls_state, NULL);
2058 /* Not connected yet so just return */
2059 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2060 GST_DEBUG_OBJECT (webrtc,
2061 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2065 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2066 sctp_transport = webrtc->priv->sctp_transport;
2068 /* Not locked state anymore so this was already taken care of before */
2069 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2072 /* Start up the SCTP elements now that the DTLS connection is established */
2073 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2074 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2076 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2077 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2079 if (sctp_transport->sctpdec_block_id) {
2080 GstPad *receive_srcpad;
2083 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2085 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2087 sctp_transport->sctpdec_block_id = 0;
2088 gst_object_unref (receive_srcpad);
2091 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2096 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2097 GParamSpec * pspec, GstWebRTCBin * webrtc)
2099 GstWebRTCDTLSTransportState dtls_state;
2101 g_object_get (transport, "state", &dtls_state, NULL);
2103 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2106 /* Connected now, so schedule a task to update the state of the SCTP
2108 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2109 gst_webrtc_bin_enqueue_task (webrtc,
2110 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2114 static GstPadProbeReturn
2115 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2117 /* Drop all events: we don't care about them and don't want to block on
2118 * them. Sticky events would be forwarded again later once we unblock
2119 * and we don't want to forward them here already because that might
2120 * cause a spurious GST_FLOW_FLUSHING */
2121 if (GST_IS_EVENT (info->data))
2122 return GST_PAD_PROBE_DROP;
2124 /* But block on any actual data-flow so we don't accidentally send that
2125 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2128 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2130 return GST_PAD_PROBE_OK;
2133 static TransportStream *
2134 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2136 if (!webrtc->priv->data_channel_transport) {
2137 TransportStream *stream;
2138 GstWebRTCSCTPTransport *sctp_transport;
2141 stream = _find_transport_for_session (webrtc, session_id);
2144 stream = _create_transport_channel (webrtc, session_id);
2145 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
2146 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
2147 g_ptr_array_add (webrtc->priv->transports, stream);
2150 webrtc->priv->data_channel_transport = stream;
2152 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2153 sctp_transport = gst_webrtc_sctp_transport_new ();
2154 sctp_transport->transport =
2155 g_object_ref (webrtc->priv->data_channel_transport->transport);
2156 sctp_transport->webrtcbin = webrtc;
2158 /* Don't automatically start SCTP elements as part of webrtcbin. We
2159 * need to delay this until the DTLS transport is fully connected! */
2160 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2161 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2163 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2164 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2167 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2168 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2169 g_signal_connect (sctp_transport, "notify::state",
2170 G_CALLBACK (_on_sctp_state_notify), webrtc);
2172 if (sctp_transport->sctpdec_block_id == 0) {
2173 GstPad *receive_srcpad;
2175 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2177 sctp_transport->sctpdec_block_id =
2178 gst_pad_add_probe (receive_srcpad,
2179 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2180 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2181 gst_object_unref (receive_srcpad);
2184 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2185 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2186 g_warn_if_reached ();
2188 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2189 GST_ELEMENT (stream->send_bin), "data_sink"))
2190 g_warn_if_reached ();
2192 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2193 WebRTCDataChannel *channel;
2195 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2197 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2200 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2201 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2203 if (!webrtc->priv->sctp_transport) {
2204 /* Connect to the notify::state signal to get notified when the DTLS
2205 * connection is established. Only then can we start the SCTP elements */
2206 g_signal_connect (stream->transport, "notify::state",
2207 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2209 /* As this would be racy otherwise, also schedule a task that checks the
2210 * current state of the connection already without getting the signal
2212 gst_webrtc_bin_enqueue_task (webrtc,
2213 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2216 webrtc->priv->sctp_transport = sctp_transport;
2217 gst_webrtc_bin_update_sctp_priority (webrtc);
2220 return webrtc->priv->data_channel_transport;
2223 static TransportStream *
2224 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2225 gboolean is_datachannel)
2228 return _get_or_create_data_channel_transports (webrtc, session_id);
2230 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2234 g_array_find_uint (GArray * array, guint val)
2238 for (i = 0; i < array->len; i++) {
2239 if (g_array_index (array, guint, i) == val)
2247 _pick_available_pt (GArray * reserved_pts, guint * i)
2249 gboolean ret = FALSE;
2251 for (*i = 96; *i <= 127; (*i)++) {
2252 if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
2253 g_array_append_val (reserved_pts, *i);
2263 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2264 GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
2265 GstSDPMedia * media)
2267 gboolean ret = TRUE;
2269 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2272 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2276 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2279 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2281 str = g_strdup_printf ("%u", pt);
2282 gst_sdp_media_add_format (media, str);
2284 str = g_strdup_printf ("%u red/%d", pt, clockrate);
2285 gst_sdp_media_add_attribute (media, "rtpmap", str);
2288 *rtx_target_pt = pt;
2290 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2293 str = g_strdup_printf ("%u", pt);
2294 gst_sdp_media_add_format (media, str);
2296 str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
2297 gst_sdp_media_add_attribute (media, "rtpmap", str);
2306 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2307 GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
2308 GstSDPMedia * media)
2310 gboolean ret = TRUE;
2312 if (trans->local_rtx_ssrc_map)
2313 gst_structure_free (trans->local_rtx_ssrc_map);
2315 trans->local_rtx_ssrc_map =
2316 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2318 if (trans->do_nack) {
2322 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2325 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2327 str = g_strdup_printf ("%u", target_ssrc);
2328 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2329 g_random_int (), NULL);
2332 str = g_strdup_printf ("%u", pt);
2333 gst_sdp_media_add_format (media, str);
2336 str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
2337 gst_sdp_media_add_attribute (media, "rtpmap", str);
2340 str = g_strdup_printf ("%u apt=%d", pt, target_pt);
2341 gst_sdp_media_add_attribute (media, "fmtp", str);
2349 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2351 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2352 GstSDPMedia * media)
2357 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2358 g_value_get_uint (value));
2359 gst_sdp_media_add_attribute (media, "ssrc-group", str);
2369 GstWebRTCBin *webrtc;
2370 WebRTCTransceiver *trans;
2374 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2380 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2381 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2382 cname = gst_structure_get_string (sdes, "cname");
2384 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2386 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2387 cname, GST_OBJECT_NAME (data->trans));
2388 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2391 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2392 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2395 gst_structure_free (sdes);
2401 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2402 WebRTCTransceiver * trans)
2405 RtxSsrcData data = { media, webrtc, trans };
2409 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2410 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2411 cname = gst_structure_get_string (sdes, "cname");
2413 if (trans->local_rtx_ssrc_map)
2414 gst_structure_foreach (trans->local_rtx_ssrc_map,
2415 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2417 for (i = 0; i < gst_caps_get_size (caps); i++) {
2418 const GstStructure *s = gst_caps_get_structure (caps, i);
2421 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2424 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2426 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2427 GST_OBJECT_NAME (trans));
2428 gst_sdp_media_add_attribute (media, "ssrc", str);
2431 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2432 gst_sdp_media_add_attribute (media, "ssrc", str);
2437 gst_structure_free (sdes);
2439 if (trans->local_rtx_ssrc_map)
2440 gst_structure_foreach (trans->local_rtx_ssrc_map,
2441 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2445 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2446 GstSDPMedia * media)
2448 gchar *cert, *fingerprint, *val;
2450 g_object_get (transport, "certificate", &cert, NULL);
2453 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2456 g_strdup_printf ("%s %s",
2457 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2458 g_free (fingerprint);
2460 gst_sdp_media_add_attribute (media, "fingerprint", val);
2464 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2466 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2467 GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
2468 GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2469 gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids)
2472 * rtp header extensions
2479 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2481 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2482 gchar *direction, *sdp_mid, *ufrag, *pwd;
2483 gboolean bundle_only;
2487 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2488 || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2491 g_assert (trans->mline == -1 || trans->mline == media_idx);
2493 bundle_only = bundled_mids && bundle_idx != media_idx
2494 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2496 /* mandated by JSEP */
2497 gst_sdp_media_add_attribute (media, "setup", "actpass");
2499 /* FIXME: deal with ICE restarts */
2500 if (last_offer && trans->mline != -1 && trans->mid) {
2501 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2502 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2503 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2505 GST_DEBUG_OBJECT (trans,
2506 "%u Generating new ice parameters mline %i, mid %s", media_idx,
2507 trans->mline, trans->mid);
2508 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2509 _generate_ice_credentials (&ufrag, &pwd);
2511 g_assert (bundle_ufrag && bundle_pwd);
2512 ufrag = g_strdup (bundle_ufrag);
2513 pwd = g_strdup (bundle_pwd);
2517 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2518 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2522 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2523 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2524 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2527 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2530 /* FIXME: negotiate this */
2531 /* FIXME: when bundle_only, these should not be added:
2532 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2533 * However, this causes incompatibilities with current versions
2534 * of the major browsers */
2535 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2536 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2539 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2541 gst_sdp_media_add_attribute (media, direction, "");
2544 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2545 caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
2547 _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
2550 g_assert_not_reached ();
2553 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2554 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2556 gst_caps_unref (caps);
2560 for (i = 0; i < gst_caps_get_size (caps); i++) {
2561 GstCaps *format = gst_caps_new_empty ();
2562 const GstStructure *s = gst_caps_get_structure (caps, i);
2564 gst_caps_append_structure (format, gst_structure_copy (s));
2566 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
2567 " to %u-th media", i, format, media_idx);
2569 /* this only looks at the first structure so we loop over the given caps
2570 * and add each structure inside it piecemeal */
2571 gst_sdp_media_set_media_from_caps (format, media);
2573 gst_caps_unref (format);
2576 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2577 const GstStructure *s = gst_caps_get_structure (caps, 0);
2578 gint clockrate = -1;
2580 gint original_rtx_target_pt; /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2581 guint rtx_target_ssrc = -1;
2583 if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
2584 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2585 g_array_append_val (reserved_pts, rtx_target_pt);
2587 original_rtx_target_pt = rtx_target_pt;
2589 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
2590 GST_WARNING_OBJECT (webrtc,
2591 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
2592 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
2593 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
2596 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2597 clockrate, &rtx_target_pt, media);
2598 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2599 clockrate, rtx_target_pt, rtx_target_ssrc, media);
2600 if (original_rtx_target_pt != rtx_target_pt)
2601 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2602 clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
2605 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
2607 /* Some identifier; we also add the media name to it so it's identifiable */
2609 gst_sdp_media_add_attribute (media, "mid", trans->mid);
2611 /* Make sure to avoid mid collisions */
2613 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2614 webrtc->priv->media_counter++);
2615 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
2618 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2619 g_hash_table_insert (all_mids, sdp_mid, NULL);
2626 * - add a=candidate lines for gathered candidates
2629 if (trans->sender) {
2630 if (!trans->sender->transport) {
2631 TransportStream *item;
2634 _get_or_create_transport_stream (webrtc,
2635 bundled_mids ? bundle_idx : media_idx, FALSE);
2637 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
2640 _add_fingerprint_to_media (trans->sender->transport, media);
2644 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2647 g_string_append_printf (bundled_mids, " %s", mid);
2650 gst_caps_unref (caps);
2656 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
2658 if (pad->received_caps) {
2659 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
2662 if (gst_structure_get_int (s, "payload", &pt)) {
2663 GST_TRACE_OBJECT (pad, "have reserved pt %u from received caps", pt);
2664 g_array_append_val (reserved_pts, pt);
2670 gather_reserved_pts (GstWebRTCBin * webrtc)
2672 GstElement *element = GST_ELEMENT (webrtc);
2673 GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2676 GST_OBJECT_LOCK (webrtc);
2677 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
2678 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
2681 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2682 GstWebRTCRTPTransceiver *trans;
2684 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
2685 if (trans->codec_preferences) {
2689 n = gst_caps_get_size (trans->codec_preferences);
2690 for (j = 0; j < n; j++) {
2691 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
2692 if (gst_structure_get_int (s, "payload", &pt)) {
2693 GST_TRACE_OBJECT (trans, "have reserved pt %u from codec preferences",
2695 g_array_append_val (reserved_pts, pt);
2700 GST_OBJECT_UNLOCK (webrtc);
2702 return reserved_pts;
2706 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
2707 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
2708 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
2710 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2711 gchar *ufrag, *pwd, *sdp_mid;
2712 gboolean bundle_only = bundled_mids
2713 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2714 && gst_sdp_message_medias_len (msg) != bundle_idx;
2715 guint last_data_index = G_MAXUINT;
2717 /* add data channel support */
2718 if (webrtc->priv->data_channels->len == 0)
2722 last_data_index = _message_get_datachannel_index (last_offer);
2723 if (last_data_index < G_MAXUINT) {
2724 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
2725 /* XXX: is this always true when recycling transceivers?
2726 * i.e. do we always put the data channel in the same mline */
2727 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
2731 /* mandated by JSEP */
2732 gst_sdp_media_add_attribute (media, "setup", "actpass");
2734 /* FIXME: only needed when restarting ICE */
2735 if (last_offer && last_data_index < G_MAXUINT) {
2736 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
2737 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
2739 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2740 _generate_ice_credentials (&ufrag, &pwd);
2742 ufrag = g_strdup (bundle_ufrag);
2743 pwd = g_strdup (bundle_pwd);
2746 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2747 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2751 gst_sdp_media_set_media (media, "application");
2752 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
2753 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2754 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2755 gst_sdp_media_add_format (media, "webrtc-datachannel");
2757 if (bundle_idx != gst_sdp_message_medias_len (msg))
2758 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2760 if (last_offer && last_data_index < G_MAXUINT) {
2761 const GstSDPMedia *last_data_media;
2764 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
2765 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
2767 gst_sdp_media_add_attribute (media, "mid", mid);
2769 /* Make sure to avoid mid collisions */
2771 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2772 webrtc->priv->media_counter++);
2773 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
2776 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2777 g_hash_table_insert (all_mids, sdp_mid, NULL);
2784 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2787 g_string_append_printf (bundled_mids, " %s", mid);
2790 /* FIXME: negotiate this properly */
2791 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2793 _get_or_create_data_channel_transports (webrtc,
2794 bundled_mids ? 0 : webrtc->priv->transceivers->len);
2795 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
2800 /* TODO: use the options argument */
2801 static GstSDPMessage *
2802 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
2805 GstSDPMessage *ret = NULL;
2806 GString *bundled_mids = NULL;
2807 gchar *bundle_ufrag = NULL;
2808 gchar *bundle_pwd = NULL;
2809 GArray *reserved_pts = NULL;
2810 GHashTable *all_mids =
2811 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
2813 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2814 GList *seen_transceivers = NULL;
2815 guint media_idx = 0;
2818 gst_sdp_message_new (&ret);
2820 gst_sdp_message_set_version (ret, "0");
2823 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
2825 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
2826 sess_id = g_strdup (origin->sess_id);
2828 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
2830 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
2834 gst_sdp_message_set_session_name (ret, "-");
2835 gst_sdp_message_add_time (ret, "0", "0", NULL);
2836 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
2838 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
2839 bundled_mids = g_string_new ("BUNDLE");
2840 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
2841 bundled_mids = g_string_new ("BUNDLE");
2844 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2845 GStrv last_bundle = NULL;
2846 guint bundle_media_index;
2848 reserved_pts = gather_reserved_pts (webrtc);
2849 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
2850 && last_bundle && last_bundle && last_bundle[0]
2851 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
2853 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
2855 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
2857 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2860 g_strfreev (last_bundle);
2863 /* FIXME: recycle transceivers */
2865 /* Fill up the renegotiated streams first */
2867 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
2868 GstWebRTCRTPTransceiver *trans = NULL;
2869 const GstSDPMedia *last_media;
2871 last_media = gst_sdp_message_get_media (last_offer, i);
2873 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
2874 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
2875 const gchar *last_mid;
2877 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
2879 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2880 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
2882 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
2885 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
2887 g_assert (!g_list_find (seen_transceivers, trans));
2889 if (wtrans->mline_locked && trans->mline != media_idx) {
2890 g_set_error (error, GST_WEBRTC_BIN_ERROR,
2891 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
2892 "Previous negotiatied transceiver %"
2893 GST_PTR_FORMAT " with mid %s was in mline %d but transceiver"
2894 " has locked mline %u", trans, trans->mid, media_idx,
2899 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
2900 GST_PTR_FORMAT " with mid %s into media index %u", trans,
2901 trans->mid, media_idx);
2903 /* FIXME: deal with format changes */
2904 gst_sdp_media_copy (last_media, &media);
2905 _media_replace_direction (media, trans->direction);
2907 mid = gst_sdp_media_get_attribute_val (media, "mid");
2910 if (g_hash_table_contains (all_mids, mid)) {
2911 gst_sdp_media_free (media);
2912 g_set_error (error, GST_WEBRTC_BIN_ERROR,
2913 GST_WEBRTC_BIN_ERROR_FAILED,
2914 "Duplicate mid %s when creating offer", mid);
2918 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
2921 g_string_append_printf (bundled_mids, " %s", mid);
2923 gst_sdp_message_add_media (ret, media);
2926 gst_sdp_media_free (media);
2927 seen_transceivers = g_list_prepend (seen_transceivers, trans);
2931 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
2932 "application") == 0) {
2933 GstSDPMedia media = { 0, };
2934 gst_sdp_media_init (&media);
2935 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
2936 bundle_ufrag, bundle_pwd, all_mids)) {
2937 gst_sdp_message_add_media (ret, &media);
2940 gst_sdp_media_uninit (&media);
2946 /* First, go over all transceivers and gather existing mids */
2947 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2948 GstWebRTCRTPTransceiver *trans;
2950 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
2952 if (g_list_find (seen_transceivers, trans))
2956 if (g_hash_table_contains (all_mids, trans->mid)) {
2957 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
2958 "Duplicate mid %s when creating offer", trans->mid);
2962 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
2967 /* add any extra streams */
2969 GstWebRTCRTPTransceiver *trans = NULL;
2970 GstSDPMedia media = { 0, };
2972 /* First find a transceiver requesting this m-line */
2973 trans = _find_transceiver_for_mline (webrtc, media_idx);
2976 /* We can't have seen it already, because it is locked to this line */
2977 g_assert (!g_list_find (seen_transceivers, trans));
2978 seen_transceivers = g_list_prepend (seen_transceivers, trans);
2980 /* Otherwise find a free transceiver */
2981 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2982 WebRTCTransceiver *wtrans;
2984 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
2985 wtrans = WEBRTC_TRANSCEIVER (trans);
2987 /* don't add transceivers twice */
2988 if (g_list_find (seen_transceivers, trans))
2991 /* Ignore transceivers with a locked mline, as they would have been
2992 * found above or will be used later */
2993 if (wtrans->mline_locked)
2996 seen_transceivers = g_list_prepend (seen_transceivers, trans);
2997 /* don't add stopped transceivers */
2998 if (trans->stopped) {
3002 /* Otherwise take it */
3006 /* Stop if we got all transceivers */
3007 if (i == webrtc->priv->transceivers->len) {
3009 /* But try to add a data channel first, we do it here, because
3010 * it can allow a locked m-line to be put after, so we need to
3011 * do another iteration after.
3013 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3014 GstSDPMedia media = { 0, };
3015 gst_sdp_media_init (&media);
3016 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3017 bundle_ufrag, bundle_pwd, all_mids)) {
3018 gst_sdp_message_add_media (ret, &media);
3022 gst_sdp_media_uninit (&media);
3026 /* Verify that we didn't ignore any locked m-line transceivers */
3027 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3028 WebRTCTransceiver *wtrans;
3030 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3031 wtrans = WEBRTC_TRANSCEIVER (trans);
3032 /* don't add transceivers twice */
3033 if (g_list_find (seen_transceivers, trans))
3035 g_assert (wtrans->mline_locked);
3037 g_set_error (error, GST_WEBRTC_BIN_ERROR,
3038 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3039 "Tranceiver %" GST_PTR_FORMAT " with mid %s has locked mline %d"
3040 " but the whole offer only has %u sections", trans, trans->mid,
3041 trans->mline, media_idx);
3048 gst_sdp_media_init (&media);
3050 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3051 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3054 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3055 "index %u", trans, media_idx);
3057 if (sdp_media_from_transceiver (webrtc, &media, trans,
3058 GST_WEBRTC_SDP_TYPE_OFFER, media_idx, bundled_mids, 0, bundle_ufrag,
3059 bundle_pwd, reserved_pts, all_mids)) {
3060 gst_sdp_message_add_media (ret, &media);
3063 gst_sdp_media_uninit (&media);
3066 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3067 g_array_free (reserved_pts, TRUE);
3068 reserved_pts = NULL;
3072 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3073 g_array_free (reserved_pts, TRUE);
3074 reserved_pts = NULL;
3077 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
3080 g_assert (media_idx == gst_sdp_message_medias_len (ret));
3083 gchar *mids = g_string_free (bundled_mids, FALSE);
3085 gst_sdp_message_add_attribute (ret, "group", mids);
3087 bundled_mids = NULL;
3090 /* FIXME: pre-emptively setup receiving elements when needed */
3092 if (webrtc->priv->last_generated_answer)
3093 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3094 webrtc->priv->last_generated_answer = NULL;
3095 if (webrtc->priv->last_generated_offer)
3096 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3098 GstSDPMessage *copy;
3099 gst_sdp_message_copy (ret, ©);
3100 webrtc->priv->last_generated_offer =
3101 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3106 g_array_free (reserved_pts, TRUE);
3108 g_hash_table_unref (all_mids);
3110 g_list_free (seen_transceivers);
3113 g_free (bundle_ufrag);
3116 g_free (bundle_pwd);
3119 g_string_free (bundled_mids, TRUE);
3124 gst_sdp_message_uninit (ret);
3130 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3131 gint * rtx_target_pt)
3135 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3138 for (i = 0; i < gst_caps_get_size (caps); i++) {
3139 const GstStructure *s = gst_caps_get_structure (caps, i);
3141 if (gst_structure_has_name (s, "application/x-rtp")) {
3142 const gchar *encoding_name =
3143 gst_structure_get_string (s, "encoding-name");
3147 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3148 gst_structure_get_int (s, "payload", &pt)) {
3149 if (!g_strcmp0 (encoding_name, "RED")) {
3152 str = g_strdup_printf ("%u", pt);
3153 gst_sdp_media_add_format (media, str);
3155 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3156 *rtx_target_pt = pt;
3157 gst_sdp_media_add_attribute (media, "rtpmap", str);
3159 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3162 str = g_strdup_printf ("%u", pt);
3163 gst_sdp_media_add_format (media, str);
3165 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3166 gst_sdp_media_add_attribute (media, "rtpmap", str);
3175 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3176 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3179 const GstStructure *s;
3181 if (trans->local_rtx_ssrc_map)
3182 gst_structure_free (trans->local_rtx_ssrc_map);
3184 trans->local_rtx_ssrc_map =
3185 gst_structure_new_empty ("application/x-rtp-ssrc-map");
3187 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
3188 s = gst_caps_get_structure (offer_caps, i);
3190 if (gst_structure_has_name (s, "application/x-rtp")) {
3191 const gchar *encoding_name =
3192 gst_structure_get_string (s, "encoding-name");
3193 const gchar *apt_str = gst_structure_get_string (s, "apt");
3201 apt = atoi (apt_str);
3203 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3204 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
3205 if (!g_strcmp0 (encoding_name, "RTX")) {
3208 str = g_strdup_printf ("%u", pt);
3209 gst_sdp_media_add_format (media, str);
3211 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
3212 gst_sdp_media_add_attribute (media, "rtpmap", str);
3215 str = g_strdup_printf ("%d apt=%d", pt, apt);
3216 gst_sdp_media_add_attribute (media, "fmtp", str);
3219 str = g_strdup_printf ("%u", target_ssrc);
3220 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
3221 g_random_int (), NULL);
3228 static GstWebRTCKind
3229 _kind_from_caps (const GstCaps * caps)
3234 if (gst_caps_get_size (caps) == 0)
3235 return GST_WEBRTC_KIND_UNKNOWN;
3237 s = gst_caps_get_structure (caps, 0);
3239 media = gst_structure_get_string (s, "media");
3241 return GST_WEBRTC_KIND_UNKNOWN;
3243 if (!g_strcmp0 (media, "audio"))
3244 return GST_WEBRTC_KIND_AUDIO;
3246 if (!g_strcmp0 (media, "video"))
3247 return GST_WEBRTC_KIND_VIDEO;
3249 return GST_WEBRTC_KIND_UNKNOWN;
3253 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
3254 const GstCaps * caps)
3256 GstWebRTCKind kind = _kind_from_caps (caps);
3258 if (trans->kind == kind)
3261 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
3270 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
3271 guint * target_ssrc)
3273 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
3275 gst_structure_get_int (s, "payload", target_pt);
3276 gst_structure_get_uint (s, "ssrc", target_ssrc);
3279 /* TODO: use the options argument */
3280 static GstSDPMessage *
3281 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3284 GstSDPMessage *ret = NULL;
3285 const GstWebRTCSessionDescription *pending_remote =
3286 webrtc->pending_remote_description;
3288 GStrv bundled = NULL;
3289 guint bundle_idx = 0;
3290 GString *bundled_mids = NULL;
3291 gchar *bundle_ufrag = NULL;
3292 gchar *bundle_pwd = NULL;
3293 GList *seen_transceivers = NULL;
3294 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
3296 if (!webrtc->pending_remote_description) {
3297 g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
3298 GST_WEBRTC_BIN_ERROR_INVALID_STATE,
3299 "Asked to create an answer without a remote description");
3303 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
3307 GStrv last_bundle = NULL;
3308 guint bundle_media_index;
3310 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
3311 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
3312 "Bundle tag is %s but no media found matching", bundled[0]);
3316 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3317 bundled_mids = g_string_new ("BUNDLE");
3320 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
3321 && last_bundle && last_bundle[0]
3322 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
3324 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
3326 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
3328 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3331 g_strfreev (last_bundle);
3334 gst_sdp_message_new (&ret);
3336 gst_sdp_message_set_version (ret, "0");
3338 const GstSDPOrigin *offer_origin =
3339 gst_sdp_message_get_origin (pending_remote->sdp);
3340 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
3341 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
3343 gst_sdp_message_set_session_name (ret, "-");
3345 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
3346 const GstSDPAttribute *attr =
3347 gst_sdp_message_get_attribute (pending_remote->sdp, i);
3349 if (g_strcmp0 (attr->key, "ice-options") == 0) {
3350 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
3354 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
3355 GstSDPMedia *media = NULL;
3356 GstSDPMedia *offer_media;
3357 GstWebRTCDTLSSetup offer_setup, answer_setup;
3359 gboolean bundle_only;
3363 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
3364 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
3366 gst_sdp_media_new (&media);
3367 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3368 gst_sdp_media_set_port_info (media, 0, 0);
3370 gst_sdp_media_set_port_info (media, 9, 0);
3371 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3376 /* FIXME: deal with ICE restarts */
3377 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
3378 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
3379 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
3382 _generate_ice_credentials (&ufrag, &pwd);
3384 ufrag = g_strdup (bundle_ufrag);
3385 pwd = g_strdup (bundle_pwd);
3388 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3389 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3394 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
3395 const GstSDPAttribute *attr =
3396 gst_sdp_media_get_attribute (offer_media, j);
3398 if (g_strcmp0 (attr->key, "mid") == 0
3399 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
3400 gst_sdp_media_add_attribute (media, attr->key, attr->value);
3401 /* FIXME: handle anything we want to keep */
3405 mid = gst_sdp_media_get_attribute_val (media, "mid");
3406 /* XXX: not strictly required but a lot of functionality requires a mid */
3409 /* set the a=setup: attribute */
3410 offer_setup = _get_dtls_setup_from_media (offer_media);
3411 answer_setup = _intersect_dtls_setup (offer_setup);
3412 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
3413 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
3414 "transceiver direction");
3417 _media_replace_setup (media, answer_setup);
3419 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
3422 if (gst_sdp_media_formats_len (offer_media) != 1) {
3423 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
3424 "for webrtc-datachannel");
3427 sctp_port = _get_sctp_port_from_media (offer_media);
3428 if (sctp_port == -1) {
3429 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
3433 /* XXX: older browsers will produce a different SDP format for data
3434 * channel that is currently not parsed correctly */
3435 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3437 gst_sdp_media_set_media (media, "application");
3438 gst_sdp_media_set_port_info (media, 9, 0);
3439 gst_sdp_media_add_format (media, "webrtc-datachannel");
3441 /* FIXME: negotiate this properly on renegotiation */
3442 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3444 _get_or_create_data_channel_transports (webrtc,
3445 bundled_mids ? bundle_idx : i);
3449 g_string_append_printf (bundled_mids, " %s", mid);
3452 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
3454 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
3455 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
3456 GstCaps *offer_caps, *answer_caps = NULL;
3457 GstWebRTCRTPTransceiver *rtp_trans = NULL;
3458 WebRTCTransceiver *trans = NULL;
3459 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
3460 gint target_pt = -1;
3461 gint original_target_pt = -1;
3462 guint target_ssrc = 0;
3464 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3465 offer_caps = _rtp_caps_from_media (offer_media);
3467 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
3469 _find_transceiver (webrtc, mid,
3470 (FindTransceiverFunc) match_for_mid))) {
3471 const GstSDPMedia *last_media =
3472 gst_sdp_message_get_media (last_answer, i);
3473 const gchar *last_mid =
3474 gst_sdp_media_get_attribute_val (last_media, "mid");
3476 /* FIXME: assumes no shenanigans with recycling transceivers */
3477 g_assert (g_strcmp0 (mid, last_mid) == 0);
3480 && (rtp_trans->direction ==
3481 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
3482 || rtp_trans->direction ==
3483 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY))
3485 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
3487 && (rtp_trans->direction ==
3488 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
3489 || rtp_trans->direction ==
3490 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY))
3492 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SRC, i);
3494 answer_caps = _rtp_caps_from_media (last_media);
3496 /* XXX: In theory we're meant to use the sendrecv formats for the
3497 * inactive direction however we don't know what that may be and would
3498 * require asking outside what it expects to possibly send later */
3500 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
3501 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
3502 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
3504 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3505 GstCaps *trans_caps;
3507 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3509 if (g_list_find (seen_transceivers, rtp_trans)) {
3510 /* Don't double allocate a transceiver to multiple mlines */
3516 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
3518 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
3519 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
3521 /* FIXME: technically this is a little overreaching as some fields we
3522 * we can deal with not having and/or we may have unrecognized fields
3523 * that we cannot actually support */
3525 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
3526 if (answer_caps && !gst_caps_is_empty (answer_caps)) {
3527 GST_LOG_OBJECT (webrtc,
3528 "found compatible transceiver %" GST_PTR_FORMAT
3529 " for offer media %u", rtp_trans, i);
3531 gst_caps_unref (trans_caps);
3535 gst_caps_unref (answer_caps);
3539 gst_caps_unref (trans_caps);
3549 answer_dir = rtp_trans->direction;
3550 g_assert (answer_caps != NULL);
3552 /* if no transceiver, then we only receive that stream and respond with
3553 * the exact same caps */
3554 /* FIXME: how to validate that subsequent elements can actually receive
3555 * this payload/format */
3556 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
3557 answer_caps = gst_caps_ref (offer_caps);
3560 if (gst_caps_is_empty (answer_caps)) {
3561 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
3563 gst_object_unref (rtp_trans);
3564 gst_caps_unref (answer_caps);
3568 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
3571 trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
3572 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
3574 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
3575 " for mline %u", trans, i);
3577 trans = WEBRTC_TRANSCEIVER (rtp_trans);
3579 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps))
3580 GST_WARNING_OBJECT (webrtc,
3581 "Trying to change transceiver %d kind from %d to %d",
3582 rtp_trans->mline, rtp_trans->kind, _kind_from_caps (answer_caps));
3584 if (!trans->do_nack) {
3585 answer_caps = gst_caps_make_writable (answer_caps);
3586 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
3587 GstStructure *s = gst_caps_get_structure (answer_caps, k);
3588 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
3592 gst_sdp_media_set_media_from_caps (answer_caps, media);
3594 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
3597 original_target_pt = target_pt;
3599 _media_add_fec (media, trans, offer_caps, &target_pt);
3600 if (trans->do_nack) {
3601 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
3602 if (target_pt != original_target_pt)
3603 _media_add_rtx (media, trans, offer_caps, original_target_pt,
3607 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
3608 _media_add_ssrcs (media, answer_caps, webrtc,
3609 WEBRTC_TRANSCEIVER (rtp_trans));
3611 gst_caps_unref (answer_caps);
3614 /* set the new media direction */
3615 offer_dir = _get_direction_from_media (offer_media);
3616 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
3617 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
3618 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
3619 "transceiver direction");
3622 _media_replace_direction (media, answer_dir);
3624 if (!trans->stream) {
3625 TransportStream *item;
3628 _get_or_create_transport_stream (webrtc,
3629 bundled_mids ? bundle_idx : i, FALSE);
3630 webrtc_transceiver_set_transport (trans, item);
3634 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3637 g_string_append_printf (bundled_mids, " %s", mid);
3640 /* set the a=fingerprint: for this transport */
3641 _add_fingerprint_to_media (trans->stream->transport, media);
3643 gst_caps_unref (offer_caps);
3645 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
3651 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
3652 gst_sdp_media_free (media);
3653 gst_sdp_media_copy (offer_media, &media);
3654 gst_sdp_media_set_port_info (media, 0, 0);
3656 gst_sdp_message_add_media (ret, media);
3657 gst_sdp_media_free (media);
3661 gchar *mids = g_string_free (bundled_mids, FALSE);
3663 gst_sdp_message_add_attribute (ret, "group", mids);
3668 g_free (bundle_ufrag);
3671 g_free (bundle_pwd);
3673 /* FIXME: can we add not matched transceivers? */
3675 /* XXX: only true for the initial offerer */
3676 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
3679 g_strfreev (bundled);
3681 g_list_free (seen_transceivers);
3683 if (webrtc->priv->last_generated_offer)
3684 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3685 webrtc->priv->last_generated_offer = NULL;
3686 if (webrtc->priv->last_generated_answer)
3687 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3689 GstSDPMessage *copy;
3690 gst_sdp_message_copy (ret, ©);
3691 webrtc->priv->last_generated_answer =
3692 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
3700 GstStructure *options;
3701 GstPromise *promise;
3702 GstWebRTCSDPType type;
3706 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
3708 GstWebRTCSessionDescription *desc = NULL;
3709 GstSDPMessage *sdp = NULL;
3710 GstStructure *s = NULL;
3711 GError *error = NULL;
3713 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
3714 gst_webrtc_sdp_type_to_string (data->type), data->options);
3716 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
3717 sdp = _create_offer_task (webrtc, data->options, &error);
3718 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
3719 sdp = _create_answer_task (webrtc, data->options, &error);
3721 g_assert_not_reached ();
3726 desc = gst_webrtc_session_description_new (data->type, sdp);
3727 s = gst_structure_new ("application/x-gst-promise",
3728 gst_webrtc_sdp_type_to_string (data->type),
3729 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
3731 g_warn_if_fail (error != NULL);
3732 GST_WARNING_OBJECT (webrtc, "returning error: %s",
3733 error ? error->message : "Unknown");
3734 s = gst_structure_new ("application/x-gstwebrtcbin-error",
3735 "error", G_TYPE_ERROR, error, NULL);
3736 g_clear_error (&error);
3741 gst_promise_reply (data->promise, s);
3745 gst_webrtc_session_description_free (desc);
3749 _free_create_sdp_data (struct create_sdp *data)
3752 gst_structure_free (data->options);
3753 gst_promise_unref (data->promise);
3758 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
3759 const GstStructure * options, GstPromise * promise)
3761 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3764 data->options = gst_structure_copy (options);
3765 data->promise = gst_promise_ref (promise);
3766 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
3768 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3769 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
3771 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
3772 "Could not create offer. webrtcbin is closed");
3774 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
3775 "error", G_TYPE_ERROR, error, NULL);
3777 gst_promise_reply (promise, s);
3779 g_clear_error (&error);
3784 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
3785 const GstStructure * options, GstPromise * promise)
3787 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3790 data->options = gst_structure_copy (options);
3791 data->promise = gst_promise_ref (promise);
3792 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
3794 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3795 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
3797 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
3798 "Could not create answer. webrtcbin is closed.");
3800 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
3801 "error", G_TYPE_ERROR, error, NULL);
3803 gst_promise_reply (promise, s);
3805 g_clear_error (&error);
3809 static GstWebRTCBinPad *
3810 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
3811 GstWebRTCRTPTransceiver * trans, guint serial)
3813 GstWebRTCBinPad *pad;
3816 if (direction == GST_PAD_SINK) {
3817 if (serial == G_MAXUINT)
3818 serial = webrtc->priv->max_sink_pad_serial++;
3820 serial = trans->mline;
3824 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
3826 pad = gst_webrtc_bin_pad_new (pad_name, direction);
3829 pad->trans = gst_object_ref (trans);
3834 static GstWebRTCRTPTransceiver *
3835 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
3836 const GstSDPMessage * sdp, guint media_idx)
3838 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3839 GstWebRTCRTPTransceiver *ret = NULL;
3842 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3843 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3845 if (g_strcmp0 (attr->key, "mid") == 0) {
3847 _find_transceiver (webrtc, attr->value,
3848 (FindTransceiverFunc) match_for_mid)))
3853 ret = _find_transceiver (webrtc, &media_idx,
3854 (FindTransceiverFunc) transceiver_match_for_mline);
3857 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
3862 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
3867 * ,-------------------------webrtcbin-------------------------,
3869 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3870 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3872 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3873 * ; sink_%u ; ; '---------------------' ;
3874 * o----------o send_rtp_sink_%u ; ;
3875 * ; '--------------------' ;
3876 * '--------------------- -------------------------------------'
3881 * ,--------------------------------webrtcbin--------------------------------,
3883 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3884 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3886 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3887 * ; sink_%u ,---funnel---, ; ; '---------------------' ;
3888 * o---------o sink_%u ; ; ; ;
3889 * ; sink_%u ; src o-o send_rtp_sink_%u ; ;
3890 * o---------o sink_%u ; ; ; ;
3891 * ; '------------' '--------------------' ;
3892 * '-------------------------------------------------------------------------'
3894 GstPadTemplate *rtp_templ;
3897 WebRTCTransceiver *trans;
3899 g_return_val_if_fail (pad->trans != NULL, NULL);
3901 trans = WEBRTC_TRANSCEIVER (pad->trans);
3903 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
3905 g_assert (trans->stream);
3907 if (!webrtc->rtpfunnel) {
3909 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
3910 "send_rtp_sink_%u");
3911 g_assert (rtp_templ);
3913 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
3915 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
3917 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
3918 gst_object_unref (rtp_sink);
3920 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
3921 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3922 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
3923 g_warn_if_reached ();
3926 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
3927 GstPad *funnel_sinkpad =
3928 gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
3930 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
3933 gst_object_unref (funnel_sinkpad);
3936 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
3938 return GST_PAD (pad);
3941 /* output pads are receiving elements */
3943 _connect_output_stream (GstWebRTCBin * webrtc,
3944 TransportStream * stream, guint session_id)
3947 * ,------------------------webrtcbin------------------------,
3948 * ; ,---------rtpbin---------, ;
3949 * ; ,-transport_receive_%u--, ; ; ;
3950 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
3952 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
3953 * ; '-----------------------' ; ; ; src_%u
3954 * ; ; recv_rtp_src_%u_%u_%u o--o
3955 * ; '------------------------' ;
3956 * '---------------------------------------------------------'
3960 if (stream->output_connected) {
3961 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
3962 "connected to rtpbin. Not connecting", stream);
3966 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
3967 session_id, stream);
3969 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
3970 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
3971 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
3972 g_warn_if_reached ();
3975 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
3977 /* The webrtcbin src_%u output pads will be created when rtpbin receives
3978 * data on that stream in on_rtpbin_pad_added() */
3980 stream->output_connected = TRUE;
3990 _clear_ice_candidate_item (IceCandidateItem * item)
3992 g_free (item->candidate);
3996 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
3997 gboolean drop_invalid)
3999 GstWebRTCICEStream *stream;
4001 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
4002 if (stream == NULL) {
4004 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
4007 IceCandidateItem new;
4008 new.mlineindex = item->mlineindex;
4009 new.candidate = g_strdup (item->candidate);
4010 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
4013 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
4014 ICE_UNLOCK (webrtc);
4019 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4020 item->mlineindex, item->candidate);
4022 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
4026 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
4027 const GstSDPMedia * media)
4030 GstWebRTCICEStream *stream = NULL;
4032 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
4033 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
4034 if (g_strcmp0 (attr->key, "candidate") == 0) {
4038 stream = _find_ice_stream_for_session (webrtc, mlineindex);
4039 if (stream == NULL) {
4040 GST_WARNING_OBJECT (webrtc,
4041 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
4045 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
4046 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4047 mlineindex, candidate);
4048 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
4055 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
4056 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
4058 GstSDPMedia *media = NULL;
4060 if (mline_index < sdp->medias->len) {
4061 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
4064 if (media == NULL) {
4065 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
4069 // Add the candidate as an attribute, first stripping off the existing
4070 // candidate: key from the string description
4071 if (strlen (candidate) < 10) {
4072 GST_WARNING_OBJECT (webrtc,
4073 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
4077 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
4081 _filter_sdp_fields (GQuark field_id, const GValue * value,
4082 GstStructure * new_structure)
4084 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
4085 gst_structure_id_set_value (new_structure, field_id, value);
4091 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
4096 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4097 GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4099 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4102 for (i = 0; i < rtx_count; i++) {
4103 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4104 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4105 const gchar *apt = gst_structure_get_string (s, "apt");
4107 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4108 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4111 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4112 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4113 stream->rtxsend, pt_map);
4115 if (stream->rtxreceive)
4116 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4117 if (stream->rtxsend)
4118 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4120 gst_structure_free (pt_map);
4125 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
4126 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
4130 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4133 proto = gst_sdp_media_get_proto (media);
4134 if (proto != NULL) {
4135 /* Parse global SDP attributes once */
4136 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
4137 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
4138 gst_sdp_message_attributes_to_caps (sdp, global_caps);
4139 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
4140 gst_sdp_media_attributes_to_caps (media, global_caps);
4142 len = gst_sdp_media_formats_len (media);
4143 for (i = 0; i < len; i++) {
4144 GstCaps *caps, *outcaps;
4150 pt = atoi (gst_sdp_media_get_format (media, i));
4152 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
4155 caps = gst_sdp_media_get_caps_from_media (media, pt);
4157 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
4161 /* Merge in global caps */
4162 /* Intersect will merge in missing fields to the current caps */
4163 outcaps = gst_caps_intersect (caps, global_caps);
4164 gst_caps_unref (caps);
4166 s = gst_caps_get_structure (outcaps, 0);
4167 gst_structure_set_name (s, "application/x-rtp");
4168 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
4169 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
4171 item.caps = gst_caps_new_empty ();
4173 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
4174 GstStructure *s = gst_caps_get_structure (outcaps, j);
4175 GstStructure *filtered =
4176 gst_structure_new_empty (gst_structure_get_name (s));
4178 gst_structure_foreach (s,
4179 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
4180 gst_caps_append_structure (item.caps, filtered);
4184 gst_caps_unref (outcaps);
4186 g_array_append_val (stream->ptmap, item);
4189 gst_caps_unref (global_caps);
4194 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
4195 const GstSDPMessage * sdp, guint media_idx,
4196 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
4197 GStrv bundled, guint bundle_idx, GError ** error)
4199 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4200 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
4201 GstWebRTCRTPTransceiverDirection new_dir;
4202 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4203 GstWebRTCDTLSSetup new_setup;
4204 gboolean new_rtcp_rsize;
4205 ReceiveState receive_state = RECEIVE_STATE_UNSET;
4208 rtp_trans->mline = media_idx;
4210 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
4211 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
4212 GST_FIXME_OBJECT (webrtc,
4213 "Updating video transceiver to audio, which isn't fully supported.");
4214 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
4217 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
4218 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
4219 GST_FIXME_OBJECT (webrtc,
4220 "Updating audio transceiver to video, which isn't fully supported.");
4221 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
4224 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4225 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4227 if (g_strcmp0 (attr->key, "mid") == 0) {
4228 g_free (rtp_trans->mid);
4229 rtp_trans->mid = g_strdup (attr->value);
4234 const GstSDPMedia *local_media, *remote_media;
4235 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
4236 GstWebRTCDTLSSetup local_setup, remote_setup;
4239 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4242 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4245 local_setup = _get_dtls_setup_from_media (local_media);
4246 remote_setup = _get_dtls_setup_from_media (remote_media);
4247 new_setup = _get_final_setup (local_setup, remote_setup);
4248 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4249 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4250 "Cannot intersect direction attributes for media %u", media_idx);
4254 local_dir = _get_direction_from_media (local_media);
4255 remote_dir = _get_direction_from_media (remote_media);
4256 new_dir = _get_final_direction (local_dir, remote_dir);
4257 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4258 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4259 "Cannot intersect dtls setup attributes for media %u", media_idx);
4263 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
4264 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
4265 && prev_dir != new_dir) {
4266 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4267 GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
4268 "transceiver direction changes are not implemented. Media %u",
4273 if (!bundled || bundle_idx == media_idx) {
4274 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
4275 && _media_has_attribute_key (remote_media, "rtcp-rsize");
4279 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
4280 media_idx, &session);
4282 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
4283 g_object_unref (session);
4289 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4291 /* Not a bundled stream means this entire transport is inactive,
4292 * so set the receive state to BLOCK below */
4293 stream->active = FALSE;
4294 receive_state = RECEIVE_STATE_BLOCK;
4297 /* If this transceiver is active for sending or receiving,
4298 * we still need receive at least RTCP, so need to unblock
4299 * the receive bin below. */
4300 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
4301 receive_state = RECEIVE_STATE_PASS;
4302 stream->active = TRUE;
4305 if (new_dir != prev_dir) {
4306 gchar *prev_dir_s, *new_dir_s;
4309 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4312 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4315 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
4316 " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
4318 g_free (prev_dir_s);
4323 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4324 GstWebRTCBinPad *pad;
4326 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
4328 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4330 GstPad *peer = gst_pad_get_peer (target);
4332 gst_pad_send_event (peer, gst_event_new_eos ());
4333 gst_object_unref (peer);
4335 gst_object_unref (target);
4337 gst_object_unref (pad);
4340 /* XXX: send eos event up the sink pad as well? */
4343 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
4344 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4345 GstWebRTCBinPad *pad =
4346 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
4348 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
4349 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4350 gst_object_unref (pad);
4352 GST_DEBUG_OBJECT (webrtc,
4353 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
4354 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
4356 _connect_input_stream (webrtc, pad);
4357 _add_pad (webrtc, pad);
4360 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
4361 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4362 GstWebRTCBinPad *pad =
4363 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4365 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
4366 " for transceiver %" GST_PTR_FORMAT, pad, trans);
4367 gst_object_unref (pad);
4369 GST_DEBUG_OBJECT (webrtc,
4370 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
4371 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
4374 if (!trans->stream) {
4375 TransportStream *item;
4378 _get_or_create_transport_stream (webrtc,
4379 bundled ? bundle_idx : media_idx, FALSE);
4380 webrtc_transceiver_set_transport (trans, item);
4383 _connect_output_stream (webrtc, trans->stream,
4384 bundled ? bundle_idx : media_idx);
4385 /* delay adding the pad until rtpbin creates the recv output pad
4386 * to ghost to so queries/events travel through the pipeline correctly
4387 * as soon as the pad is added */
4388 _add_pad_to_list (webrtc, pad);
4393 rtp_trans->mline = media_idx;
4394 rtp_trans->current_direction = new_dir;
4397 if (!bundled || bundle_idx == media_idx) {
4398 if (stream->rtxsend || stream->rtxreceive) {
4399 _set_rtx_ptmap_from_stream (webrtc, stream);
4402 g_object_set (stream, "dtls-client",
4403 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4406 /* Must be after setting the "dtls-client" so that data is not pushed into
4407 * the dtlssrtp elements before the ssl direction has been set which will
4408 * throw SSL errors */
4409 if (receive_state != RECEIVE_STATE_UNSET)
4410 transport_receive_bin_set_receive_state (stream->receive_bin,
4414 /* must be called with the pc lock held */
4416 _generate_data_channel_id (GstWebRTCBin * webrtc)
4419 gint new_id = -1, max_channels = 0;
4421 if (webrtc->priv->sctp_transport) {
4422 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
4425 if (max_channels <= 0) {
4426 max_channels = 65534;
4429 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
4432 /* TODO: a better search algorithm */
4434 WebRTCDataChannel *channel;
4438 if (new_id < 0 || new_id >= max_channels) {
4439 /* exhausted id space */
4440 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
4441 "data channel id (max %i)", max_channels);
4445 /* client must generate even ids, server must generate odd ids */
4446 if (new_id % 2 == ! !is_client)
4449 channel = _find_data_channel_for_id (webrtc, new_id);
4458 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
4459 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
4462 const GstSDPMedia *local_media, *remote_media;
4463 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
4464 TransportReceiveBin *receive;
4465 int local_port, remote_port;
4466 guint64 local_max_size, remote_max_size, max_size;
4470 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4473 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4476 local_setup = _get_dtls_setup_from_media (local_media);
4477 remote_setup = _get_dtls_setup_from_media (remote_media);
4478 new_setup = _get_final_setup (local_setup, remote_setup);
4479 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4480 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4481 "Cannot intersect dtls setup for media %u", media_idx);
4485 /* data channel is always rtcp-muxed to avoid generating ICE candidates
4487 g_object_set (stream, "dtls-client",
4488 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4490 local_port = _get_sctp_port_from_media (local_media);
4491 remote_port = _get_sctp_port_from_media (local_media);
4492 if (local_port == -1 || remote_port == -1) {
4493 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4494 "Could not find sctp port for media %u (local %i, remote %i)",
4495 media_idx, local_port, remote_port);
4499 if (0 == (local_max_size =
4500 _get_sctp_max_message_size_from_media (local_media)))
4501 local_max_size = G_MAXUINT64;
4502 if (0 == (remote_max_size =
4503 _get_sctp_max_message_size_from_media (remote_media)))
4504 remote_max_size = G_MAXUINT64;
4505 max_size = MIN (local_max_size, remote_max_size);
4507 webrtc->priv->sctp_transport->max_message_size = max_size;
4510 guint orig_local_port, orig_remote_port;
4512 /* XXX: sctpassociation warns if we are in the wrong state */
4513 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
4514 &orig_local_port, NULL);
4516 if (orig_local_port != local_port)
4517 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
4520 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
4521 &orig_remote_port, NULL);
4522 if (orig_remote_port != remote_port)
4523 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
4527 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
4528 WebRTCDataChannel *channel;
4530 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
4532 if (channel->parent.id == -1)
4533 channel->parent.id = _generate_data_channel_id (webrtc);
4534 if (channel->parent.id == -1)
4535 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
4536 ("%s", "Failed to generate an identifier for a data channel"), NULL);
4538 if (webrtc->priv->sctp_transport->association_established
4539 && !channel->parent.negotiated && !channel->opened) {
4540 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
4541 webrtc_data_channel_start_negotiation (channel);
4545 stream->active = TRUE;
4547 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
4548 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
4552 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
4555 GstWebRTCKind kind = GPOINTER_TO_INT (data);
4559 if (p1->mline != -1)
4563 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
4570 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
4573 GstPad *queue_srcpad;
4575 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
4580 if (webrtc->rtpfunnel)
4583 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
4584 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
4585 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
4587 queue = gst_element_factory_make ("queue", NULL);
4588 gst_bin_add (GST_BIN (webrtc), queue);
4589 gst_element_sync_state_with_parent (queue);
4591 gst_element_link (webrtc->rtpfunnel, queue);
4593 queue_srcpad = gst_element_get_static_pad (queue, "src");
4595 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
4596 rtp_sink = gst_element_get_request_pad (webrtc->rtpbin, pad_name);
4598 gst_pad_link (queue_srcpad, rtp_sink);
4599 gst_object_unref (queue_srcpad);
4600 gst_object_unref (rtp_sink);
4602 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
4603 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4604 GST_ELEMENT (stream->send_bin), "rtp_sink"))
4605 g_warn_if_reached ();
4613 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
4614 GstWebRTCSessionDescription * sdp, GError ** error)
4617 gboolean ret = FALSE;
4618 GStrv bundled = NULL;
4619 guint bundle_idx = 0;
4620 TransportStream *bundle_stream = NULL;
4622 /* FIXME: With some peers, it's possible we could have
4623 * multiple bundles to deal with, although I've never seen one yet */
4624 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
4625 if (!_parse_bundle (sdp->sdp, &bundled, error))
4630 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
4631 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4632 "Bundle tag is %s but no media found matching", bundled[0]);
4636 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
4637 _message_media_is_datachannel (sdp->sdp, bundle_idx));
4638 /* Mark the bundle stream as inactive to start. It will be set to TRUE
4639 * by any bundled mline that is active, and at the end we set the
4640 * receivebin to BLOCK if all mlines were inactive. */
4641 bundle_stream->active = FALSE;
4643 g_array_set_size (bundle_stream->ptmap, 0);
4644 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4645 /* When bundling, we need to do this up front, or else RTX
4646 * parameters aren't set up properly for the bundled streams */
4647 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
4650 _connect_rtpfunnel (webrtc, bundle_idx);
4653 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4654 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
4655 TransportStream *stream;
4656 GstWebRTCRTPTransceiver *trans;
4657 guint transport_idx;
4659 /* skip rejected media */
4660 if (gst_sdp_media_get_port (media) == 0)
4664 transport_idx = bundle_idx;
4668 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
4670 stream = _get_or_create_transport_stream (webrtc, transport_idx,
4671 _message_media_is_datachannel (sdp->sdp, transport_idx));
4673 /* When bundling, these were all set up above, but when not
4674 * bundling we need to do it now */
4675 g_array_set_size (stream->ptmap, 0);
4676 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
4680 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
4682 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
4683 g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4684 "State mismatch. Could not find local transceiver by mline %u", i);
4687 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
4688 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
4689 /* No existing transceiver, find an unused one */
4693 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
4694 kind = GST_WEBRTC_KIND_AUDIO;
4696 kind = GST_WEBRTC_KIND_VIDEO;
4698 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
4699 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
4702 /* Still no transceiver? Create one */
4703 /* XXX: default to the advertised direction in the sdp for new
4704 * transceivers. The spec doesn't actually say what happens here, only
4705 * that calls to setDirection will change the value. Nothing about
4706 * a default value when the transceiver is created internally */
4708 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
4709 _get_direction_from_media (media), i);
4710 webrtc_transceiver_set_transport (t, stream);
4711 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
4714 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
4715 trans, bundled, bundle_idx, error);
4716 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
4717 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
4720 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
4725 if (bundle_stream && bundle_stream->active == FALSE) {
4726 /* No bundled mline marked the bundle as active, so block the receive bin, as
4727 * this bundle is completely inactive */
4728 GST_LOG_OBJECT (webrtc,
4729 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
4730 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
4731 RECEIVE_STATE_BLOCK);
4737 g_strfreev (bundled);
4743 check_transceivers_not_removed (GstWebRTCBin * webrtc,
4744 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
4749 if (gst_sdp_message_medias_len (previous->sdp) >
4750 gst_sdp_message_medias_len (new->sdp))
4757 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
4762 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
4763 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
4764 GstWebRTCRTPTransceiver *rtp_trans;
4765 WebRTCTransceiver *trans;
4767 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
4768 /* only look for matching mid */
4769 if (rtp_trans == NULL)
4772 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4774 /* We only validate the locked mlines for now */
4775 if (!trans->mline_locked)
4778 if (rtp_trans->mline != i) {
4779 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4780 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
4781 "m-line with mid %s is at position %d, but was locked to %d, "
4782 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
4786 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
4787 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
4788 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
4789 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4790 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
4791 "m-line %d was locked to audio, but SDP has %s media", i,
4792 gst_sdp_media_get_media (media));
4796 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
4797 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
4798 g_set_error (error, GST_WEBRTC_BIN_ERROR,
4799 GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
4800 "m-line %d was locked to video, but SDP has %s media", i,
4801 gst_sdp_media_get_media (media));
4811 struct set_description
4813 GstPromise *promise;
4815 GstWebRTCSessionDescription *sdp;
4818 static GstWebRTCSessionDescription *
4819 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
4820 GstWebRTCSDPType type)
4823 case GST_WEBRTC_SDP_TYPE_OFFER:
4824 case GST_WEBRTC_SDP_TYPE_PRANSWER:
4825 case GST_WEBRTC_SDP_TYPE_ANSWER:
4826 if (source == SDP_LOCAL) {
4827 return webrtc->current_local_description;
4829 return webrtc->current_remote_description;
4831 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
4834 /* other values mean memory corruption/uninitialized! */
4835 g_assert_not_reached ();
4842 /* http://w3c.github.io/webrtc-pc/#set-description */
4844 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
4846 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
4847 gboolean signalling_state_changed = FALSE;
4848 GError *error = NULL;
4849 GStrv bundled = NULL;
4850 guint bundle_idx = 0;
4854 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4855 webrtc->signaling_state);
4857 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
4858 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
4859 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
4860 _sdp_source_to_string (sd->source), type_str, state);
4861 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
4867 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
4870 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
4871 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
4875 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
4876 g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4877 "Bundle tag is %s but no matching media found", bundled[0]);
4882 if (!check_transceivers_not_removed (webrtc,
4883 get_previous_description (webrtc, sd->source, sd->sdp->type),
4885 g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR,
4886 GST_WEBRTC_BIN_ERROR_BAD_SDP,
4887 "m=lines removed from the SDP. Processing a completely new connection "
4888 "is not currently supported.");
4892 if (!check_locked_mlines (webrtc, sd->sdp, &error))
4895 switch (sd->sdp->type) {
4896 case GST_WEBRTC_SDP_TYPE_OFFER:{
4897 if (sd->source == SDP_LOCAL) {
4898 if (webrtc->pending_local_description)
4899 gst_webrtc_session_description_free
4900 (webrtc->pending_local_description);
4901 webrtc->pending_local_description =
4902 gst_webrtc_session_description_copy (sd->sdp);
4903 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
4905 if (webrtc->pending_remote_description)
4906 gst_webrtc_session_description_free
4907 (webrtc->pending_remote_description);
4908 webrtc->pending_remote_description =
4909 gst_webrtc_session_description_copy (sd->sdp);
4910 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
4914 case GST_WEBRTC_SDP_TYPE_ANSWER:{
4915 if (sd->source == SDP_LOCAL) {
4916 if (webrtc->current_local_description)
4917 gst_webrtc_session_description_free
4918 (webrtc->current_local_description);
4919 webrtc->current_local_description =
4920 gst_webrtc_session_description_copy (sd->sdp);
4922 if (webrtc->current_remote_description)
4923 gst_webrtc_session_description_free
4924 (webrtc->current_remote_description);
4925 webrtc->current_remote_description = webrtc->pending_remote_description;
4926 webrtc->pending_remote_description = NULL;
4928 if (webrtc->current_remote_description)
4929 gst_webrtc_session_description_free
4930 (webrtc->current_remote_description);
4931 webrtc->current_remote_description =
4932 gst_webrtc_session_description_copy (sd->sdp);
4934 if (webrtc->current_local_description)
4935 gst_webrtc_session_description_free
4936 (webrtc->current_local_description);
4937 webrtc->current_local_description = webrtc->pending_local_description;
4938 webrtc->pending_local_description = NULL;
4941 if (webrtc->pending_local_description)
4942 gst_webrtc_session_description_free (webrtc->pending_local_description);
4943 webrtc->pending_local_description = NULL;
4945 if (webrtc->pending_remote_description)
4946 gst_webrtc_session_description_free
4947 (webrtc->pending_remote_description);
4948 webrtc->pending_remote_description = NULL;
4950 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4953 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
4954 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
4955 if (sd->source == SDP_LOCAL) {
4956 if (webrtc->pending_local_description)
4957 gst_webrtc_session_description_free
4958 (webrtc->pending_local_description);
4959 webrtc->pending_local_description = NULL;
4961 if (webrtc->pending_remote_description)
4962 gst_webrtc_session_description_free
4963 (webrtc->pending_remote_description);
4964 webrtc->pending_remote_description = NULL;
4967 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4970 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
4971 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
4972 if (sd->source == SDP_LOCAL) {
4973 if (webrtc->pending_local_description)
4974 gst_webrtc_session_description_free
4975 (webrtc->pending_local_description);
4976 webrtc->pending_local_description =
4977 gst_webrtc_session_description_copy (sd->sdp);
4979 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
4981 if (webrtc->pending_remote_description)
4982 gst_webrtc_session_description_free
4983 (webrtc->pending_remote_description);
4984 webrtc->pending_remote_description =
4985 gst_webrtc_session_description_copy (sd->sdp);
4987 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
4993 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
4995 * If the mid value of an RTCRtpTransceiver was set to a non-null value
4996 * by the RTCSessionDescription that is being rolled back, set the mid
4997 * value of that transceiver to null, as described by [JSEP]
4998 * (section 4.1.7.2.).
4999 * If an RTCRtpTransceiver was created by applying the
5000 * RTCSessionDescription that is being rolled back, and a track has not
5001 * been attached to it via addTrack, remove that transceiver from
5002 * connection's set of transceivers, as described by [JSEP]
5003 * (section 4.1.7.2.).
5004 * Restore the value of connection's [[ sctpTransport]] internal slot
5005 * to its value at the last stable signaling state.
5009 if (webrtc->signaling_state != new_signaling_state) {
5010 webrtc->signaling_state = new_signaling_state;
5011 signalling_state_changed = TRUE;
5015 gboolean ice_controller = FALSE;
5017 /* get the current value so we don't change ice controller from TRUE to
5018 * FALSE on renegotiation or once set to TRUE for the initial local offer */
5019 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
5021 /* we control ice negotiation if we send the initial offer */
5023 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
5024 && webrtc->current_remote_description == NULL;
5025 /* or, if the remote is an ice-lite peer */
5026 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
5027 && webrtc->current_remote_description
5028 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
5031 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
5032 ice_controller ? "true" : "false");
5033 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
5036 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5039 /* media modifications */
5040 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
5043 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
5044 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
5045 GstWebRTCRTPTransceiverDirection new_dir;
5047 const GstSDPMedia *media;
5049 if (!pad->received_caps) {
5050 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
5055 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
5056 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
5061 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
5062 /* skip rejected media */
5063 if (gst_sdp_media_get_port (media) == 0) {
5064 /* FIXME: arrange for an appropriate flow return */
5065 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
5066 "a more correct flow return.");
5072 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
5077 new_dir = pad->trans->direction;
5078 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
5079 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5080 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
5081 "data at the moment. Not connecting input stream yet", pad->trans);
5086 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
5087 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
5088 pad->trans, pad->received_caps);
5089 _connect_input_stream (webrtc, pad);
5090 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
5094 gst_object_unref (old->data);
5095 webrtc->priv->pending_sink_transceivers =
5096 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
5100 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
5101 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
5103 TransportStream *item;
5106 _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
5107 _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
5109 if (sd->source == SDP_REMOTE) {
5112 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
5113 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
5115 if (g_strcmp0 (attr->key, "ssrc") == 0) {
5116 GStrv split = g_strsplit (attr->value, " ", 0);
5119 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
5120 && g_str_has_prefix (split[1], "cname:")) {
5121 g_ptr_array_add (item->remote_ssrcmap, ssrcmap_item_new (ssrc, i));
5128 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
5129 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5131 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
5132 item->stream, ufrag, pwd);
5135 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
5136 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5138 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
5139 item->stream, ufrag, pwd);
5145 if (sd->source == SDP_LOCAL) {
5146 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
5147 IceStreamItem *item =
5148 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
5150 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
5154 /* Add any pending trickle ICE candidates if we have both offer and answer */
5155 if (webrtc->current_local_description && webrtc->current_remote_description) {
5158 GstWebRTCSessionDescription *remote_sdp =
5159 webrtc->current_remote_description;
5161 /* Add any remote ICE candidates from the remote description to
5162 * support non-trickle peers first */
5163 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
5164 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
5165 _add_ice_candidates_from_sdp (webrtc, i, media);
5169 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
5170 IceCandidateItem *item =
5171 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
5172 IceCandidateItem, i);
5174 _add_ice_candidate (webrtc, item, TRUE);
5176 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
5177 ICE_UNLOCK (webrtc);
5181 * If connection's signaling state changed above, fire an event named
5182 * signalingstatechange at connection.
5184 if (signalling_state_changed) {
5185 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5186 webrtc->signaling_state);
5187 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5188 new_signaling_state);
5189 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
5192 g_object_notify (G_OBJECT (webrtc), "signaling-state");
5199 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5200 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
5202 /* If connection's signaling state is now stable, update the
5203 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
5204 * was true both before and after this update, queue a task to check
5205 * connection's [[needNegotiation]] slot and, if still true, fire a
5206 * simple event named negotiationneeded at connection.*/
5207 _update_need_negotiation (webrtc);
5208 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
5209 _check_need_negotiation_task (webrtc, NULL);
5214 g_strfreev (bundled);
5218 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
5219 gst_promise_reply (sd->promise,
5220 gst_structure_new ("application/x-gstwebrtcbin-error", "error",
5221 G_TYPE_ERROR, error, NULL));
5222 g_clear_error (&error);
5224 gst_promise_reply (sd->promise, NULL);
5230 _free_set_description_data (struct set_description *sd)
5233 gst_promise_unref (sd->promise);
5235 gst_webrtc_session_description_free (sd->sdp);
5240 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
5241 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
5243 struct set_description *sd;
5245 if (remote_sdp == NULL)
5247 if (remote_sdp->sdp == NULL)
5250 sd = g_new0 (struct set_description, 1);
5251 if (promise != NULL)
5252 sd->promise = gst_promise_ref (promise);
5253 sd->source = SDP_REMOTE;
5254 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
5256 if (!gst_webrtc_bin_enqueue_task (webrtc,
5257 (GstWebRTCBinFunc) _set_description_task, sd,
5258 (GDestroyNotify) _free_set_description_data, promise)) {
5260 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5261 "Could not set remote description. webrtcbin is closed.");
5263 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
5264 "error", G_TYPE_ERROR, error, NULL);
5266 gst_promise_reply (promise, s);
5268 g_clear_error (&error);
5275 gst_promise_reply (promise, NULL);
5276 g_return_if_reached ();
5281 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
5282 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
5284 struct set_description *sd;
5286 if (local_sdp == NULL)
5288 if (local_sdp->sdp == NULL)
5291 sd = g_new0 (struct set_description, 1);
5292 if (promise != NULL)
5293 sd->promise = gst_promise_ref (promise);
5294 sd->source = SDP_LOCAL;
5295 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
5297 if (!gst_webrtc_bin_enqueue_task (webrtc,
5298 (GstWebRTCBinFunc) _set_description_task, sd,
5299 (GDestroyNotify) _free_set_description_data, promise)) {
5301 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5302 "Could not set remote description. webrtcbin is closed");
5304 gst_structure_new ("application/x-gstwebrtcbin-promise-error",
5305 "error", G_TYPE_ERROR, error, NULL);
5307 gst_promise_reply (promise, s);
5309 g_clear_error (&error);
5316 gst_promise_reply (promise, NULL);
5317 g_return_if_reached ();
5322 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
5324 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
5325 IceCandidateItem new;
5326 new.mlineindex = item->mlineindex;
5327 new.candidate = g_steal_pointer (&item->candidate);
5330 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5331 ICE_UNLOCK (webrtc);
5333 _add_ice_candidate (webrtc, item, FALSE);
5338 _free_ice_candidate_item (IceCandidateItem * item)
5340 _clear_ice_candidate_item (item);
5345 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
5348 IceCandidateItem *item;
5350 item = g_new0 (IceCandidateItem, 1);
5351 item->mlineindex = mline;
5352 if (attr && attr[0] != 0) {
5353 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
5354 item->candidate = g_strdup (attr);
5355 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
5356 item->candidate = g_strdup_printf ("a=%s", attr);
5358 gst_webrtc_bin_enqueue_task (webrtc,
5359 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
5360 (GDestroyNotify) _free_ice_candidate_item, NULL);
5364 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
5370 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
5371 ICE_UNLOCK (webrtc);
5372 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
5373 return; /* Nothing to process */
5375 /* Take the array so we can process it all and free it later
5376 * without holding the lock
5377 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
5379 items = webrtc->priv->pending_local_ice_candidates;
5380 /* Replace with a new array */
5381 webrtc->priv->pending_local_ice_candidates =
5382 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
5383 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
5384 (GDestroyNotify) _clear_ice_candidate_item);
5385 ICE_UNLOCK (webrtc);
5387 for (i = 0; i < items->len; i++) {
5388 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
5389 const gchar *cand = item->candidate;
5391 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
5392 /* stripping away "a=" */
5396 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
5397 item->mlineindex, cand);
5399 /* First, merge this ice candidate into the appropriate mline
5400 * in the local-description SDP.
5401 * Second, emit the on-ice-candidate signal for the app.
5403 * FIXME: This ICE candidate should be stored somewhere with
5404 * the associated mid and also merged back into any subsequent
5405 * local descriptions on renegotiation */
5406 if (webrtc->current_local_description)
5407 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
5408 item->mlineindex, cand);
5409 if (webrtc->pending_local_description)
5410 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
5411 item->mlineindex, cand);
5414 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
5415 0, item->mlineindex, cand);
5419 g_array_free (items, TRUE);
5423 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
5424 gchar * candidate, GstWebRTCBin * webrtc)
5426 IceCandidateItem item;
5427 gboolean queue_task = FALSE;
5429 item.mlineindex = session_id;
5430 item.candidate = g_strdup (candidate);
5433 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
5435 /* Let the first pending candidate queue a task each time, which will
5436 * handle any that arrive between now and when the task runs */
5437 if (webrtc->priv->pending_local_ice_candidates->len == 1)
5439 ICE_UNLOCK (webrtc);
5442 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
5443 gst_webrtc_bin_enqueue_task (webrtc,
5444 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
5451 GstPromise *promise;
5455 _free_get_stats (struct get_stats *stats)
5458 gst_object_unref (stats->pad);
5460 gst_promise_unref (stats->promise);
5464 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
5466 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
5469 /* Our selector is the pad,
5470 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
5473 s = gst_webrtc_bin_create_stats (webrtc, stats->pad);
5476 gst_promise_reply (stats->promise, s);
5481 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
5482 GstPromise * promise)
5484 struct get_stats *stats;
5486 g_return_if_fail (promise != NULL);
5487 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
5489 stats = g_new0 (struct get_stats, 1);
5490 stats->promise = gst_promise_ref (promise);
5491 /* FIXME: check that pad exists in element */
5493 stats->pad = gst_object_ref (pad);
5495 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
5496 stats, (GDestroyNotify) _free_get_stats, promise)) {
5498 g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5499 "Could not retrieve statistics. webrtcbin is closed.");
5500 GstStructure *s = gst_structure_new ("application/x-gst-promise-error",
5501 "error", G_TYPE_ERROR, error, NULL);
5503 gst_promise_reply (promise, s);
5505 g_clear_error (&error);
5509 static GstWebRTCRTPTransceiver *
5510 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
5511 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
5513 WebRTCTransceiver *trans;
5514 GstWebRTCRTPTransceiver *rtp_trans;
5516 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
5521 trans = _create_webrtc_transceiver (webrtc, direction, -1);
5522 GST_LOG_OBJECT (webrtc,
5523 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
5525 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
5527 rtp_trans->codec_preferences = gst_caps_ref (caps);
5528 _update_transceiver_kind_from_caps (rtp_trans, caps);
5533 return gst_object_ref (trans);
5537 _deref_and_unref (GstObject ** object)
5539 gst_clear_object (object);
5543 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
5545 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
5550 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
5552 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5553 GstWebRTCRTPTransceiver *trans =
5554 g_ptr_array_index (webrtc->priv->transceivers, i);
5555 gst_object_ref (trans);
5556 g_array_append_val (arr, trans);
5563 static GstWebRTCRTPTransceiver *
5564 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
5566 GstWebRTCRTPTransceiver *trans = NULL;
5570 if (idx >= webrtc->priv->transceivers->len) {
5571 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
5575 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
5576 gst_object_ref (trans);
5584 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
5588 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
5589 g_return_val_if_fail (uri != NULL, FALSE);
5591 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
5594 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
5601 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
5603 GstPad *gpad = GST_PAD_CAST (user_data);
5605 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
5606 gst_pad_store_sticky_event (gpad, *event);
5611 static WebRTCDataChannel *
5612 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
5613 GstStructure * init_params)
5616 gint max_packet_lifetime;
5617 gint max_retransmits;
5618 const gchar *protocol;
5619 gboolean negotiated;
5621 GstWebRTCPriorityType priority;
5622 WebRTCDataChannel *ret;
5623 gint max_channels = 65534;
5625 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
5626 g_return_val_if_fail (label != NULL, NULL);
5627 g_return_val_if_fail (strlen (label) <= 65535, NULL);
5628 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
5631 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
5634 || !gst_structure_get_int (init_params, "max-packet-lifetime",
5635 &max_packet_lifetime))
5636 max_packet_lifetime = -1;
5638 || !gst_structure_get_int (init_params, "max-retransmits",
5640 max_retransmits = -1;
5641 /* both retransmits and lifetime cannot be set */
5642 g_return_val_if_fail ((max_packet_lifetime == -1)
5643 || (max_retransmits == -1), NULL);
5646 || !(protocol = gst_structure_get_string (init_params, "protocol")))
5648 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
5651 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
5653 if (!negotiated || !init_params
5654 || !gst_structure_get_int (init_params, "id", &id))
5657 g_return_val_if_fail (id != -1, NULL);
5658 g_return_val_if_fail (id < 65535, NULL);
5661 || !gst_structure_get_enum (init_params, "priority",
5662 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
5663 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
5665 /* FIXME: clamp max-retransmits and max-packet-lifetime */
5667 if (webrtc->priv->sctp_transport) {
5668 /* Let transport be the connection's [[SctpTransport]] slot.
5670 * If the [[DataChannelId]] slot is not null, transport is in
5671 * connected state and [[DataChannelId]] is greater or equal to the
5672 * transport's [[MaxChannels]] slot, throw an OperationError.
5674 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5677 g_return_val_if_fail (id <= max_channels, NULL);
5680 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
5681 !_have_sctp_elements (webrtc))
5685 /* check if the id has been used already */
5687 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
5689 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
5690 ("Attempting to add a data channel with a duplicate ID: %i", id),
5695 } else if (webrtc->current_local_description
5696 && webrtc->current_remote_description && webrtc->priv->sctp_transport
5697 && webrtc->priv->sctp_transport->transport) {
5698 /* else we can only generate an id if we're configured already. The other
5699 * case for generating an id is on sdp setting */
5700 id = _generate_data_channel_id (webrtc);
5702 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5703 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5709 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
5710 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
5711 "max-retransmits", max_retransmits, "protocol", protocol,
5712 "negotiated", negotiated, "id", id, "priority", priority, NULL);
5715 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
5716 gst_bin_add (GST_BIN (webrtc), ret->appsink);
5718 gst_element_sync_state_with_parent (ret->appsrc);
5719 gst_element_sync_state_with_parent (ret->appsink);
5721 ret = gst_object_ref (ret);
5722 ret->webrtcbin = webrtc;
5723 g_ptr_array_add (webrtc->priv->data_channels, ret);
5724 gst_webrtc_bin_update_sctp_priority (webrtc);
5725 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
5726 if (webrtc->priv->sctp_transport &&
5727 webrtc->priv->sctp_transport->association_established
5728 && !ret->parent.negotiated) {
5729 webrtc_data_channel_start_negotiation (ret);
5731 _update_need_negotiation (webrtc);
5739 /* === rtpbin signal implementations === */
5742 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
5743 GstWebRTCBin * webrtc)
5745 gchar *new_pad_name = NULL;
5747 new_pad_name = gst_pad_get_name (new_pad);
5748 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
5749 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
5750 guint32 session_id = 0, ssrc = 0, pt = 0;
5751 GstWebRTCRTPTransceiver *rtp_trans;
5752 WebRTCTransceiver *trans;
5753 TransportStream *stream;
5754 GstWebRTCBinPad *pad;
5755 guint media_idx = 0;
5756 gboolean found_ssrc = FALSE;
5759 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
5761 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
5765 stream = _find_transport_for_session (webrtc, session_id);
5767 g_warn_if_reached ();
5769 media_idx = session_id;
5771 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
5772 SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
5773 if (item->ssrc == ssrc) {
5774 media_idx = item->media_idx;
5781 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
5784 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
5786 g_warn_if_reached ();
5787 trans = WEBRTC_TRANSCEIVER (rtp_trans);
5788 g_assert (trans->stream == stream);
5790 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5792 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
5793 " for rtpbin pad name %s", pad, new_pad_name);
5795 g_warn_if_reached ();
5796 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
5798 if (webrtc->priv->running)
5799 gst_pad_set_active (GST_PAD (pad), TRUE);
5800 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
5801 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
5802 _remove_pending_pad (webrtc, pad);
5804 gst_object_unref (pad);
5806 g_free (new_pad_name);
5809 /* only used for the receiving streams */
5811 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
5812 GstWebRTCBin * webrtc)
5814 TransportStream *stream;
5817 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
5820 stream = _find_transport_for_session (webrtc, session_id);
5822 goto unknown_session;
5824 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
5827 GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
5828 "session %d", ret, pt, session_id);
5834 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
5840 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
5842 GstStructure *s = user_data;
5844 gst_structure_id_set_value (s, field_id, value);
5850 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
5851 GstWebRTCBin * webrtc)
5853 TransportStream *stream;
5854 gboolean have_rtx = FALSE;
5855 GstStructure *pt_map = NULL;
5856 GstElement *ret = NULL;
5858 stream = _find_transport_for_session (webrtc, session_id);
5861 have_rtx = transport_stream_get_pt (stream, "RTX") != 0;
5863 GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
5864 " with pt map %" GST_PTR_FORMAT, stream, pt_map);
5870 GstStructure *merged_local_rtx_ssrc_map =
5871 gst_structure_new_empty ("application/x-rtp-ssrc-map");
5874 if (stream->rtxsend) {
5875 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
5879 GST_INFO ("creating AUX sender");
5880 ret = gst_bin_new (NULL);
5881 rtx = gst_element_factory_make ("rtprtxsend", NULL);
5882 g_object_set (rtx, "max-size-packets", 500, NULL);
5883 _set_rtx_ptmap_from_stream (webrtc, stream);
5885 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5886 WebRTCTransceiver *trans =
5887 WEBRTC_TRANSCEIVER (g_ptr_array_index (webrtc->priv->transceivers,
5890 if (trans->stream == stream && trans->local_rtx_ssrc_map)
5891 gst_structure_foreach (trans->local_rtx_ssrc_map,
5892 _merge_structure, merged_local_rtx_ssrc_map);
5895 g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
5896 gst_structure_free (merged_local_rtx_ssrc_map);
5898 gst_bin_add (GST_BIN (ret), rtx);
5900 pad = gst_element_get_static_pad (rtx, "src");
5901 name = g_strdup_printf ("src_%u", session_id);
5902 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
5904 gst_object_unref (pad);
5906 pad = gst_element_get_static_pad (rtx, "sink");
5907 name = g_strdup_printf ("sink_%u", session_id);
5908 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
5910 gst_object_unref (pad);
5912 stream->rtxsend = gst_object_ref (rtx);
5917 gst_structure_free (pt_map);
5923 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
5924 GstWebRTCBin * webrtc)
5926 GstElement *ret = NULL;
5927 GstElement *prev = NULL;
5928 GstPad *sinkpad = NULL;
5929 TransportStream *stream;
5933 stream = _find_transport_for_session (webrtc, session_id);
5936 red_pt = transport_stream_get_pt (stream, "RED");
5937 rtx_pt = transport_stream_get_pt (stream, "RTX");
5940 GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
5943 if (red_pt || rtx_pt)
5944 ret = gst_bin_new (NULL);
5947 if (stream->rtxreceive) {
5948 GST_WARNING_OBJECT (webrtc,
5949 "rtprtxreceive already created! rtpbin bug?!");
5953 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
5954 _set_rtx_ptmap_from_stream (webrtc, stream);
5956 gst_bin_add (GST_BIN (ret), stream->rtxreceive);
5958 sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
5960 prev = gst_object_ref (stream->rtxreceive);
5964 GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
5966 GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
5967 red_pt, session_id);
5969 gst_bin_add (GST_BIN (ret), rtpreddec);
5971 g_object_set (rtpreddec, "pt", red_pt, NULL);
5974 gst_element_link (prev, rtpreddec);
5976 sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
5982 gchar *name = g_strdup_printf ("sink_%u", session_id);
5983 GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
5985 gst_object_unref (sinkpad);
5986 gst_element_add_pad (ret, ghost);
5990 gchar *name = g_strdup_printf ("src_%u", session_id);
5991 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
5992 GstPad *ghost = gst_ghost_pad_new (name, srcpad);
5994 gst_object_unref (srcpad);
5995 gst_element_add_pad (ret, ghost);
6003 gst_object_unref (ret);
6008 on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
6009 GstWebRTCBin * webrtc)
6011 TransportStream *stream;
6012 GstElement *ret = NULL;
6014 GObject *internal_storage;
6016 stream = _find_transport_for_session (webrtc, session_id);
6018 /* TODO: for now, we only support ulpfec, but once we support
6019 * more algorithms, if the remote may use more than one algorithm,
6020 * we will want to do the following:
6022 * + Return a bin here, with the relevant FEC decoders plugged in
6023 * and their payload type set to 0
6024 * + Enable the decoders by setting the payload type only when
6025 * we detect it (by connecting to ptdemux:new-payload-type for
6029 pt = transport_stream_get_pt (stream, "ULPFEC");
6032 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
6034 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
6035 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
6038 g_object_set (ret, "pt", pt, "storage", internal_storage, NULL);
6039 g_object_unref (internal_storage);
6046 on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
6047 GstWebRTCBin * webrtc)
6049 GstElement *ret = NULL;
6050 GstElement *prev = NULL;
6051 TransportStream *stream;
6052 guint ulpfec_pt = 0;
6054 GstPad *sinkpad = NULL;
6055 GstWebRTCRTPTransceiver *trans;
6057 stream = _find_transport_for_session (webrtc, session_id);
6058 trans = _find_transceiver (webrtc, &session_id,
6059 (FindTransceiverFunc) transceiver_match_for_mline);
6062 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
6063 red_pt = transport_stream_get_pt (stream, "RED");
6066 if (ulpfec_pt || red_pt)
6067 ret = gst_bin_new (NULL);
6070 GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
6071 GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
6073 GST_DEBUG_OBJECT (webrtc,
6074 "Creating ULPFEC encoder for session %d with pt %d", session_id,
6077 gst_bin_add (GST_BIN (ret), fecenc);
6078 sinkpad = gst_element_get_static_pad (fecenc, "sink");
6079 g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
6080 WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
6083 if (caps && !gst_caps_is_empty (caps)) {
6084 const GstStructure *s = gst_caps_get_structure (caps, 0);
6085 const gchar *media = gst_structure_get_string (s, "media");
6087 if (!g_strcmp0 (media, "video"))
6088 g_object_set (fecenc, "multipacket", TRUE, NULL);
6095 GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
6097 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
6098 session_id, red_pt);
6100 gst_bin_add (GST_BIN (ret), redenc);
6102 gst_element_link (prev, redenc);
6104 sinkpad = gst_element_get_static_pad (redenc, "sink");
6106 g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
6112 GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
6113 gst_object_unref (sinkpad);
6114 gst_element_add_pad (ret, ghost);
6118 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
6119 GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
6120 gst_object_unref (srcpad);
6121 gst_element_add_pad (ret, ghost);
6128 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6129 GstWebRTCBin * webrtc)
6131 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
6135 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6136 GstWebRTCBin * webrtc)
6138 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
6142 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6143 GstWebRTCBin * webrtc)
6145 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
6150 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6151 GstWebRTCBin * webrtc)
6153 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
6157 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6158 GstWebRTCBin * webrtc)
6160 GST_INFO_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
6164 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
6165 GstWebRTCBin * webrtc)
6167 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
6171 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
6172 GstWebRTCBin * webrtc)
6174 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
6178 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
6179 GstWebRTCBin * webrtc)
6181 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
6185 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6186 GstWebRTCBin * webrtc)
6188 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
6192 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6193 GstWebRTCBin * webrtc)
6195 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
6200 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6201 GstWebRTCBin * webrtc)
6203 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
6208 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
6209 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
6211 WebRTCTransceiver *trans;
6214 trans = (WebRTCTransceiver *) _find_transceiver (webrtc, &session_id,
6215 (FindTransceiverFunc) transceiver_match_for_mline);
6218 /* We don't set do-retransmission on rtpbin as we want per-session control */
6219 g_object_set (jitterbuffer, "do-retransmission",
6220 WEBRTC_TRANSCEIVER (trans)->do_nack, NULL);
6222 for (i = 0; i < trans->stream->remote_ssrcmap->len; i++) {
6223 SsrcMapItem *item = g_ptr_array_index (trans->stream->remote_ssrcmap, i);
6225 if (item->ssrc == ssrc) {
6226 g_weak_ref_set (&item->rtpjitterbuffer, jitterbuffer);
6231 g_assert_not_reached ();
6236 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
6237 guint session_id, GstWebRTCBin * webrtc)
6239 guint64 latency = webrtc->priv->jb_latency;
6241 /* Add an extra 50 ms for safey */
6242 latency += RTPSTORAGE_EXTRA_TIME;
6243 latency *= GST_MSECOND;
6245 g_object_set (storage, "size-time", latency, NULL);
6249 _create_rtpbin (GstWebRTCBin * webrtc)
6253 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
6256 /* mandated by WebRTC */
6257 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
6259 g_object_set (rtpbin, "do-lost", TRUE, NULL);
6261 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
6263 g_signal_connect (rtpbin, "request-pt-map",
6264 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
6265 g_signal_connect (rtpbin, "request-aux-sender",
6266 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
6267 g_signal_connect (rtpbin, "request-aux-receiver",
6268 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
6269 g_signal_connect (rtpbin, "new-storage",
6270 G_CALLBACK (on_rtpbin_new_storage), webrtc);
6271 g_signal_connect (rtpbin, "request-fec-decoder",
6272 G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
6273 g_signal_connect (rtpbin, "request-fec-encoder",
6274 G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
6275 g_signal_connect (rtpbin, "on-bye-ssrc",
6276 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
6277 g_signal_connect (rtpbin, "on-bye-timeout",
6278 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
6279 g_signal_connect (rtpbin, "on-new-ssrc",
6280 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
6281 g_signal_connect (rtpbin, "on-new-sender-ssrc",
6282 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
6283 g_signal_connect (rtpbin, "on-sender-ssrc-active",
6284 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
6285 g_signal_connect (rtpbin, "on-sender-timeout",
6286 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
6287 g_signal_connect (rtpbin, "on-ssrc-active",
6288 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
6289 g_signal_connect (rtpbin, "on-ssrc-collision",
6290 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
6291 g_signal_connect (rtpbin, "on-ssrc-sdes",
6292 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
6293 g_signal_connect (rtpbin, "on-ssrc-validated",
6294 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
6295 g_signal_connect (rtpbin, "on-timeout",
6296 G_CALLBACK (on_rtpbin_timeout), webrtc);
6297 g_signal_connect (rtpbin, "new-jitterbuffer",
6298 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
6303 static GstStateChangeReturn
6304 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
6306 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6307 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
6309 GST_DEBUG ("changing state: %s => %s",
6310 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
6311 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
6313 switch (transition) {
6314 case GST_STATE_CHANGE_NULL_TO_READY:{
6315 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6316 return GST_STATE_CHANGE_FAILURE;
6317 _start_thread (webrtc);
6319 _update_need_negotiation (webrtc);
6323 case GST_STATE_CHANGE_READY_TO_PAUSED:
6324 webrtc->priv->running = TRUE;
6330 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6331 if (ret == GST_STATE_CHANGE_FAILURE)
6334 switch (transition) {
6335 case GST_STATE_CHANGE_READY_TO_PAUSED:
6336 /* Mangle the return value to NO_PREROLL as that's what really is
6337 * occurring here however cannot be propagated correctly due to nicesrc
6338 * requiring that it be in PLAYING already in order to send/receive
6340 ret = GST_STATE_CHANGE_NO_PREROLL;
6342 case GST_STATE_CHANGE_PAUSED_TO_READY:
6343 webrtc->priv->running = FALSE;
6345 case GST_STATE_CHANGE_READY_TO_NULL:
6346 _stop_thread (webrtc);
6355 static GstPadProbeReturn
6356 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
6358 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
6360 return GST_PAD_PROBE_OK;
6365 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
6366 const gchar * name, const GstCaps * caps)
6368 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6369 GstWebRTCRTPTransceiver *trans = NULL;
6370 GstWebRTCBinPad *pad = NULL;
6372 gboolean lock_mline = FALSE;
6374 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6377 if (templ->direction != GST_PAD_SINK ||
6378 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
6379 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
6385 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
6386 /* no name given when requesting the pad, use next available int */
6387 serial = webrtc->priv->max_sink_pad_serial++;
6389 /* parse serial number from requested padname */
6390 serial = g_ascii_strtoull (&name[5], NULL, 10);
6395 GstWebRTCBinPad *pad2;
6397 trans = _find_transceiver_for_mline (webrtc, serial);
6400 /* Reject transceivers that are only for receiving ... */
6401 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
6402 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
6404 g_enum_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
6406 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6407 " existing m-line %d, but the transceiver's direction is %s",
6408 name, serial, direction);
6413 /* Reject transceivers that already have a pad allocated */
6414 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
6416 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
6417 " but the transceiver associated with this m-line already has pad"
6418 " %s", name, serial, GST_PAD_NAME (pad2));
6419 gst_object_unref (pad2);
6424 if (trans->codec_preferences &&
6425 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
6426 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6427 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
6428 " don't match existing codec preferences %" GST_PTR_FORMAT,
6429 name, serial, caps, trans->codec_preferences);
6433 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
6434 GstWebRTCKind kind = _kind_from_caps (caps);
6436 if (trans->kind != kind) {
6437 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6438 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
6439 " don't match transceiver kind %d",
6440 name, serial, caps, trans->kind);
6448 /* Let's try to find a free transceiver that matches */
6450 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
6454 kind = _kind_from_caps (caps);
6456 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6457 GstWebRTCRTPTransceiver *tmptrans =
6458 g_ptr_array_index (webrtc->priv->transceivers, i);
6459 GstWebRTCBinPad *pad2;
6461 /* Ignore transceivers with a non-matching kind */
6462 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
6463 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
6466 /* Ignore stopped transmitters */
6467 if (tmptrans->stopped)
6470 /* Ignore transceivers that are only for receiving ... */
6471 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
6472 || tmptrans->direction ==
6473 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
6476 /* Ignore transceivers that already have a pad allocated */
6477 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
6479 gst_object_unref (pad2);
6483 /* Ignore transceivers with non-matching caps */
6484 if (caps && tmptrans->codec_preferences &&
6485 !gst_caps_can_intersect (caps, tmptrans->codec_preferences)) {
6495 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
6496 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1));
6497 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
6499 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
6500 " for mline %u", trans, serial);
6502 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
6505 _update_transceiver_kind_from_caps (trans, caps);
6507 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
6508 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
6509 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
6510 webrtc->priv->pending_sink_transceivers =
6511 g_list_append (webrtc->priv->pending_sink_transceivers,
6512 gst_object_ref (pad));
6515 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
6516 wtrans->mline_locked = TRUE;
6517 trans->mline = serial;
6522 _add_pad (webrtc, pad);
6524 return GST_PAD (pad);
6532 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
6534 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6535 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
6537 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
6539 /* remove the transceiver from the pad so that subsequent code doesn't use
6540 * a possibly dead transceiver */
6542 if (webrtc_pad->trans)
6543 gst_object_unref (webrtc_pad->trans);
6544 webrtc_pad->trans = NULL;
6545 gst_caps_replace (&webrtc_pad->received_caps, NULL);
6548 _remove_pad (webrtc, webrtc_pad);
6551 _update_need_negotiation (webrtc);
6556 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
6561 /* Add an extra 50 ms for safety */
6562 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
6563 latency_ns *= GST_MSECOND;
6565 for (i = 0; i < webrtc->priv->transports->len; i++) {
6566 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
6567 GObject *storage = NULL;
6569 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
6572 g_object_set (storage, "size-time", latency_ns, NULL);
6574 g_object_unref (storage);
6579 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
6580 const GValue * value, GParamSpec * pspec)
6582 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6585 case PROP_STUN_SERVER:
6586 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
6587 g_value_get_string (value));
6589 case PROP_TURN_SERVER:
6590 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
6591 g_value_get_string (value));
6593 case PROP_BUNDLE_POLICY:
6594 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
6595 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
6597 webrtc->bundle_policy = g_value_get_enum (value);
6600 case PROP_ICE_TRANSPORT_POLICY:
6601 webrtc->ice_transport_policy = g_value_get_enum (value);
6602 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
6603 webrtc->ice_transport_policy ==
6604 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
6607 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
6608 webrtc->priv->jb_latency = g_value_get_uint (value);
6609 _update_rtpstorage_latency (webrtc);
6612 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
6618 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
6619 GValue * value, GParamSpec * pspec)
6621 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6625 case PROP_CONNECTION_STATE:
6626 g_value_set_enum (value, webrtc->peer_connection_state);
6628 case PROP_SIGNALING_STATE:
6629 g_value_set_enum (value, webrtc->signaling_state);
6631 case PROP_ICE_GATHERING_STATE:
6632 g_value_set_enum (value, webrtc->ice_gathering_state);
6634 case PROP_ICE_CONNECTION_STATE:
6635 g_value_set_enum (value, webrtc->ice_connection_state);
6637 case PROP_LOCAL_DESCRIPTION:
6638 if (webrtc->pending_local_description)
6639 g_value_set_boxed (value, webrtc->pending_local_description);
6640 else if (webrtc->current_local_description)
6641 g_value_set_boxed (value, webrtc->current_local_description);
6643 g_value_set_boxed (value, NULL);
6645 case PROP_CURRENT_LOCAL_DESCRIPTION:
6646 g_value_set_boxed (value, webrtc->current_local_description);
6648 case PROP_PENDING_LOCAL_DESCRIPTION:
6649 g_value_set_boxed (value, webrtc->pending_local_description);
6651 case PROP_REMOTE_DESCRIPTION:
6652 if (webrtc->pending_remote_description)
6653 g_value_set_boxed (value, webrtc->pending_remote_description);
6654 else if (webrtc->current_remote_description)
6655 g_value_set_boxed (value, webrtc->current_remote_description);
6657 g_value_set_boxed (value, NULL);
6659 case PROP_CURRENT_REMOTE_DESCRIPTION:
6660 g_value_set_boxed (value, webrtc->current_remote_description);
6662 case PROP_PENDING_REMOTE_DESCRIPTION:
6663 g_value_set_boxed (value, webrtc->pending_remote_description);
6665 case PROP_STUN_SERVER:
6666 g_value_take_string (value,
6667 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
6669 case PROP_TURN_SERVER:
6670 g_value_take_string (value,
6671 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
6673 case PROP_BUNDLE_POLICY:
6674 g_value_set_enum (value, webrtc->bundle_policy);
6676 case PROP_ICE_TRANSPORT_POLICY:
6677 g_value_set_enum (value, webrtc->ice_transport_policy);
6679 case PROP_ICE_AGENT:
6680 g_value_set_object (value, webrtc->priv->ice);
6683 g_value_set_uint (value, webrtc->priv->jb_latency);
6686 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
6693 gst_webrtc_bin_constructed (GObject * object)
6695 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6698 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
6699 webrtc->priv->ice = gst_webrtc_ice_new (name);
6701 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
6702 (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
6708 _free_pending_pad (GstPad * pad)
6710 gst_object_unref (pad);
6714 gst_webrtc_bin_dispose (GObject * object)
6716 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6718 if (webrtc->priv->ice)
6719 gst_object_unref (webrtc->priv->ice);
6720 webrtc->priv->ice = NULL;
6722 if (webrtc->priv->ice_stream_map)
6723 g_array_free (webrtc->priv->ice_stream_map, TRUE);
6724 webrtc->priv->ice_stream_map = NULL;
6726 g_clear_object (&webrtc->priv->sctp_transport);
6728 G_OBJECT_CLASS (parent_class)->dispose (object);
6732 gst_webrtc_bin_finalize (GObject * object)
6734 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
6736 if (webrtc->priv->transports)
6737 g_ptr_array_free (webrtc->priv->transports, TRUE);
6738 webrtc->priv->transports = NULL;
6740 if (webrtc->priv->transceivers)
6741 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
6742 webrtc->priv->transceivers = NULL;
6744 if (webrtc->priv->data_channels)
6745 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
6746 webrtc->priv->data_channels = NULL;
6748 if (webrtc->priv->pending_data_channels)
6749 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
6750 webrtc->priv->pending_data_channels = NULL;
6752 if (webrtc->priv->pending_remote_ice_candidates)
6753 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
6754 webrtc->priv->pending_remote_ice_candidates = NULL;
6756 if (webrtc->priv->pending_local_ice_candidates)
6757 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
6758 webrtc->priv->pending_local_ice_candidates = NULL;
6760 if (webrtc->priv->pending_pads)
6761 g_list_free_full (webrtc->priv->pending_pads,
6762 (GDestroyNotify) _free_pending_pad);
6763 webrtc->priv->pending_pads = NULL;
6765 if (webrtc->priv->pending_sink_transceivers)
6766 g_list_free_full (webrtc->priv->pending_sink_transceivers,
6767 (GDestroyNotify) gst_object_unref);
6768 webrtc->priv->pending_sink_transceivers = NULL;
6770 if (webrtc->current_local_description)
6771 gst_webrtc_session_description_free (webrtc->current_local_description);
6772 webrtc->current_local_description = NULL;
6773 if (webrtc->pending_local_description)
6774 gst_webrtc_session_description_free (webrtc->pending_local_description);
6775 webrtc->pending_local_description = NULL;
6777 if (webrtc->current_remote_description)
6778 gst_webrtc_session_description_free (webrtc->current_remote_description);
6779 webrtc->current_remote_description = NULL;
6780 if (webrtc->pending_remote_description)
6781 gst_webrtc_session_description_free (webrtc->pending_remote_description);
6782 webrtc->pending_remote_description = NULL;
6784 if (webrtc->priv->last_generated_answer)
6785 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
6786 webrtc->priv->last_generated_answer = NULL;
6787 if (webrtc->priv->last_generated_offer)
6788 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
6789 webrtc->priv->last_generated_offer = NULL;
6791 g_mutex_clear (ICE_GET_LOCK (webrtc));
6792 g_mutex_clear (PC_GET_LOCK (webrtc));
6793 g_cond_clear (PC_GET_COND (webrtc));
6795 G_OBJECT_CLASS (parent_class)->finalize (object);
6799 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
6801 GObjectClass *gobject_class = (GObjectClass *) klass;
6802 GstElementClass *element_class = (GstElementClass *) klass;
6804 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
6805 element_class->release_pad = gst_webrtc_bin_release_pad;
6806 element_class->change_state = gst_webrtc_bin_change_state;
6808 gst_element_class_add_static_pad_template_with_gtype (element_class,
6809 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
6810 gst_element_class_add_static_pad_template (element_class, &src_template);
6812 gst_element_class_set_metadata (element_class, "WebRTC Bin",
6813 "Filter/Network/WebRTC", "A bin for webrtc connections",
6814 "Matthew Waters <matthew@centricular.com>");
6816 gobject_class->constructed = gst_webrtc_bin_constructed;
6817 gobject_class->get_property = gst_webrtc_bin_get_property;
6818 gobject_class->set_property = gst_webrtc_bin_set_property;
6819 gobject_class->dispose = gst_webrtc_bin_dispose;
6820 gobject_class->finalize = gst_webrtc_bin_finalize;
6822 g_object_class_install_property (gobject_class,
6823 PROP_LOCAL_DESCRIPTION,
6824 g_param_spec_boxed ("local-description", "Local Description",
6825 "The local SDP description in use for this connection. "
6826 "Favours a pending description over the current description",
6827 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6828 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6830 g_object_class_install_property (gobject_class,
6831 PROP_CURRENT_LOCAL_DESCRIPTION,
6832 g_param_spec_boxed ("current-local-description",
6833 "Current Local Description",
6834 "The local description that was successfully negotiated the last time "
6835 "the connection transitioned into the stable state",
6836 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6837 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6839 g_object_class_install_property (gobject_class,
6840 PROP_PENDING_LOCAL_DESCRIPTION,
6841 g_param_spec_boxed ("pending-local-description",
6842 "Pending Local Description",
6843 "The local description that is in the process of being negotiated plus "
6844 "any local candidates that have been generated by the ICE Agent since the "
6845 "offer or answer was created",
6846 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6847 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6849 g_object_class_install_property (gobject_class,
6850 PROP_REMOTE_DESCRIPTION,
6851 g_param_spec_boxed ("remote-description", "Remote Description",
6852 "The remote SDP description to use for this connection. "
6853 "Favours a pending description over the current description",
6854 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6855 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6857 g_object_class_install_property (gobject_class,
6858 PROP_CURRENT_REMOTE_DESCRIPTION,
6859 g_param_spec_boxed ("current-remote-description",
6860 "Current Remote Description",
6861 "The last remote description that was successfully negotiated the last "
6862 "time the connection transitioned into the stable state plus any remote "
6863 "candidates that have been supplied via addIceCandidate() since the offer "
6864 "or answer was created",
6865 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6866 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6868 g_object_class_install_property (gobject_class,
6869 PROP_PENDING_REMOTE_DESCRIPTION,
6870 g_param_spec_boxed ("pending-remote-description",
6871 "Pending Remote Description",
6872 "The remote description that is in the process of being negotiated, "
6873 "complete with any remote candidates that have been supplied via "
6874 "addIceCandidate() since the offer or answer was created",
6875 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
6876 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6878 g_object_class_install_property (gobject_class,
6880 g_param_spec_string ("stun-server", "STUN Server",
6881 "The STUN server of the form stun://hostname:port",
6882 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6884 g_object_class_install_property (gobject_class,
6886 g_param_spec_string ("turn-server", "TURN Server",
6887 "The TURN server of the form turn(s)://username:password@host:port. "
6888 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
6889 "if you wish to use multiple TURN servers",
6890 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6892 g_object_class_install_property (gobject_class,
6893 PROP_CONNECTION_STATE,
6894 g_param_spec_enum ("connection-state", "Connection State",
6895 "The overall connection state of this element",
6896 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
6897 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
6898 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6900 g_object_class_install_property (gobject_class,
6901 PROP_SIGNALING_STATE,
6902 g_param_spec_enum ("signaling-state", "Signaling State",
6903 "The signaling state of this element",
6904 GST_TYPE_WEBRTC_SIGNALING_STATE,
6905 GST_WEBRTC_SIGNALING_STATE_STABLE,
6906 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6908 g_object_class_install_property (gobject_class,
6909 PROP_ICE_CONNECTION_STATE,
6910 g_param_spec_enum ("ice-connection-state", "ICE connection state",
6911 "The collective connection state of all ICETransport's",
6912 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
6913 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
6914 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6916 g_object_class_install_property (gobject_class,
6917 PROP_ICE_GATHERING_STATE,
6918 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
6919 "The collective gathering state of all ICETransport's",
6920 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
6921 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
6922 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6924 g_object_class_install_property (gobject_class,
6926 g_param_spec_enum ("bundle-policy", "Bundle Policy",
6927 "The policy to apply for bundling",
6928 GST_TYPE_WEBRTC_BUNDLE_POLICY,
6929 GST_WEBRTC_BUNDLE_POLICY_NONE,
6930 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6932 g_object_class_install_property (gobject_class,
6933 PROP_ICE_TRANSPORT_POLICY,
6934 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
6935 "The policy to apply for ICE transport",
6936 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
6937 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
6938 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6940 g_object_class_install_property (gobject_class,
6942 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
6943 "The WebRTC ICE agent",
6944 GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
6947 * GstWebRTCBin:latency:
6949 * Default duration to buffer in the jitterbuffers (in ms)
6954 g_object_class_install_property (gobject_class,
6956 g_param_spec_uint ("latency", "Latency",
6957 "Default duration to buffer in the jitterbuffers (in ms)",
6958 0, G_MAXUINT, 200, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
6961 * GstWebRTCBin::create-offer:
6962 * @object: the #webrtcbin
6963 * @options: (nullable): create-offer options
6964 * @promise: a #GstPromise which will contain the offer
6966 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
6967 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
6968 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6969 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
6970 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
6973 * GstWebRTCBin::create-answer:
6974 * @object: the #webrtcbin
6975 * @options: (nullable): create-answer options
6976 * @promise: a #GstPromise which will contain the answer
6978 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
6979 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
6980 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6981 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
6982 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
6985 * GstWebRTCBin::set-local-description:
6986 * @object: the #GstWebRTCBin
6987 * @desc: a #GstWebRTCSessionDescription description
6988 * @promise: (nullable): a #GstPromise to be notified when it's set
6990 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
6991 g_signal_new_class_handler ("set-local-description",
6992 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
6993 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
6994 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
6997 * GstWebRTCBin::set-remote-description:
6998 * @object: the #GstWebRTCBin
6999 * @desc: a #GstWebRTCSessionDescription description
7000 * @promise: (nullable): a #GstPromise to be notified when it's set
7002 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
7003 g_signal_new_class_handler ("set-remote-description",
7004 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7005 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
7006 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7009 * GstWebRTCBin::add-ice-candidate:
7010 * @object: the #webrtcbin
7011 * @mline_index: the index of the media description in the SDP
7012 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
7015 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
7016 g_signal_new_class_handler ("add-ice-candidate",
7017 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7018 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
7019 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7022 * GstWebRTCBin::get-stats:
7023 * @object: the #webrtcbin
7024 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
7025 * @promise: a #GstPromise for the result
7027 * The @promise will contain the result of retrieving the session statistics.
7028 * The structure will be named 'application/x-webrtc-stats and contain the
7029 * following based on the webrtc-stats spec available from
7030 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
7031 * and is constantly changing these statistics may be changed to fit with
7034 * Each field key is a unique identifier for each RTCStats
7035 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
7036 * GstStructure) in the RTCStatsReport
7037 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
7038 * field in the RTCStats subclass is outlined below.
7040 * Each statistics structure contains the following values as defined by
7041 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
7043 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
7044 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
7045 * "id" G_TYPE_STRING unique identifier
7047 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
7049 * "payload-type" G_TYPE_UINT the rtp payload number in use
7050 * "clock-rate" G_TYPE_UINT the rtp clock-rate
7052 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
7054 * "ssrc" G_TYPE_STRING the rtp sequence src in use
7055 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
7056 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
7057 * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
7058 * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
7059 * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
7061 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
7063 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
7064 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
7065 * "packets-lost" G_TYPE_UINT number of packets lost
7066 * "jitter" G_TYPE_DOUBLE packet jitter measured in secondss
7068 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
7070 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
7072 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
7074 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
7075 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
7077 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
7079 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7080 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
7082 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
7084 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
7086 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
7088 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
7091 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
7092 g_signal_new_class_handler ("get-stats",
7093 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7094 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
7095 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
7098 * GstWebRTCBin::on-negotiation-needed:
7099 * @object: the #webrtcbin
7101 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
7102 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
7103 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
7106 * GstWebRTCBin::on-ice-candidate:
7107 * @object: the #webrtcbin
7108 * @mline_index: the index of the media description in the SDP
7109 * @candidate: the ICE candidate
7111 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
7112 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
7113 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7114 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7117 * GstWebRTCBin::on-new-transceiver:
7118 * @object: the #webrtcbin
7119 * @candidate: the new #GstWebRTCRTPTransceiver
7121 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
7122 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
7123 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7124 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
7127 * GstWebRTCBin::on-data-channel:
7128 * @object: the #GstWebRTCBin
7129 * @candidate: the new `GstWebRTCDataChannel`
7131 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
7132 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
7133 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7134 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
7137 * GstWebRTCBin::add-transceiver:
7138 * @object: the #webrtcbin
7139 * @direction: the direction of the new transceiver
7140 * @caps: (allow none): the codec preferences for this transceiver
7142 * Returns: the new #GstWebRTCRTPTransceiver
7144 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
7145 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
7146 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7147 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
7148 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
7149 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
7152 * GstWebRTCBin::get-transceivers:
7153 * @object: the #webrtcbin
7155 * Returns: a #GArray of #GstWebRTCRTPTransceivers
7157 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
7158 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
7159 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7160 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
7164 * GstWebRTCBin::get-transceiver:
7165 * @object: the #GstWebRTCBin
7166 * @idx: The index of the transceiver
7168 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
7171 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
7172 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
7173 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7174 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
7175 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
7178 * GstWebRTCBin::add-turn-server:
7179 * @object: the #GstWebRTCBin
7180 * @uri: The uri of the server of the form turn(s)://username:password@host:port
7182 * Add a turn server to obtain ICE candidates from
7184 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
7185 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
7186 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7187 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
7188 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
7191 * GstWebRTCBin::create-data-channel:
7192 * @object: the #GstWebRTCBin
7193 * @label: the label for the data channel
7194 * @options: a #GstStructure of options for creating the data channel
7196 * The options dictionary is the same format as the RTCDataChannelInit
7197 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
7198 * and reproduced below
7200 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
7201 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
7202 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
7203 * protocol G_TYPE_STRING The subprotocol used by this channel
7204 * 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.
7205 * id G_TYPE_INT Override the default identifier selection of this channel
7206 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
7208 * Returns: (transfer full): a new data channel object
7210 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
7211 g_signal_new_class_handler ("create-data-channel",
7212 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7213 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
7214 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
7216 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
7217 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
7221 _unparent_and_unref (GObject * object)
7223 GstObject *obj = GST_OBJECT (object);
7225 GST_OBJECT_PARENT (obj) = NULL;
7227 gst_object_unref (obj);
7231 _transport_free (GObject * object)
7233 TransportStream *stream = (TransportStream *) object;
7234 GstWebRTCBin *webrtc;
7236 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
7238 if (stream->transport) {
7239 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
7240 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
7243 gst_object_unref (object);
7247 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
7249 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
7250 g_mutex_init (PC_GET_LOCK (webrtc));
7251 g_cond_init (PC_GET_COND (webrtc));
7253 g_mutex_init (ICE_GET_LOCK (webrtc));
7255 webrtc->rtpbin = _create_rtpbin (webrtc);
7256 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
7258 webrtc->priv->transceivers =
7259 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
7260 webrtc->priv->transports =
7261 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
7263 webrtc->priv->data_channels =
7264 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7266 webrtc->priv->pending_data_channels =
7267 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7269 webrtc->priv->ice_stream_map =
7270 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
7271 webrtc->priv->pending_remote_ice_candidates =
7272 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7273 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
7274 (GDestroyNotify) _clear_ice_candidate_item);
7276 webrtc->priv->pending_local_ice_candidates =
7277 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7278 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
7279 (GDestroyNotify) _clear_ice_candidate_item);
7281 /* we start off closed until we move to READY */
7282 webrtc->priv->is_closed = TRUE;