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"
38 #define RANDOM_SESSION_ID \
39 ((((((guint64) g_random_int()) << 32) | \
40 (guint64) g_random_int ())) & \
41 G_GUINT64_CONSTANT (0x7fffffffffffffff))
43 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
44 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
45 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
47 #define PC_GET_COND(w) (&w->priv->pc_cond)
48 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
49 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
50 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
53 * This webrtcbin implements the majority of the W3's peerconnection API and
54 * implementation guide where possible. Generating offers, answers and setting
55 * local and remote SDP's are all supported. Both media descriptions and
56 * descriptions involving data channels are supported.
58 * Each input/output pad is equivalent to a Track in W3 parlance which are
59 * added/removed from the bin. The number of requested sink pads is the number
60 * of streams that will be sent to the receiver and will be associated with a
61 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
63 * On the receiving side, RTPTransceiver's are created in response to setting
64 * a remote description. Output pads for the receiving streams in the set
65 * description are also created when data is received.
67 * A TransportStream is created when needed in order to transport the data over
68 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
69 * on the negotiated SDP's between the peers based on the bundle and rtcp
70 * configuration. Some cases are outlined below for a simple single
71 * audio/video/data session:
73 * - max-bundle (requires rtcp-muxing) uses a single transport for all
74 * media/data transported. Renegotiation involves adding/removing the
75 * necessary streams to the existing transports.
76 * - max-compat without rtcp-mux involves two TransportStream per media stream
77 * to transport the rtp and the rtcp packets and a single TransportStream for
78 * all data channels. Each stream change involves modifying the associated
79 * TransportStream/s as necessary.
84 * assert sending payload type matches the stream
85 * reconfiguration (of anything)
87 * balanced bundle policy
88 * setting custom DTLS certificates
90 * separate session id's from mlineindex properly
91 * how to deal with replacing a input/output track/stream
94 static void _update_need_negotiation (GstWebRTCBin * webrtc);
96 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
97 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
101 PROP_PAD_TRANSCEIVER = 1,
105 _have_nice_elements (GstWebRTCBin * webrtc)
107 GstPluginFeature *feature;
109 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
111 gst_object_unref (feature);
113 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
114 ("%s", "libnice elements are not available"));
118 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
120 gst_object_unref (feature);
122 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
123 ("%s", "libnice elements are not available"));
131 _have_sctp_elements (GstWebRTCBin * webrtc)
133 GstPluginFeature *feature;
135 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
137 gst_object_unref (feature);
139 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
140 ("%s", "sctp elements are not available"));
144 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
146 gst_object_unref (feature);
148 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
149 ("%s", "sctp elements are not available"));
157 _have_dtls_elements (GstWebRTCBin * webrtc)
159 GstPluginFeature *feature;
161 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
163 gst_object_unref (feature);
165 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
166 ("%s", "dtls elements are not available"));
170 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
172 gst_object_unref (feature);
174 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
175 ("%s", "dtls elements are not available"));
182 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
185 gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
186 const GValue * value, GParamSpec * pspec)
190 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
197 GValue * value, GParamSpec * pspec)
199 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
202 case PROP_PAD_TRANSCEIVER:
203 g_value_set_object (value, pad->trans);
206 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
212 gst_webrtc_bin_pad_finalize (GObject * object)
214 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
217 gst_object_unref (pad->trans);
220 if (pad->received_caps)
221 gst_caps_unref (pad->received_caps);
222 pad->received_caps = NULL;
224 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
228 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
230 GObjectClass *gobject_class = (GObjectClass *) klass;
232 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
233 gobject_class->set_property = gst_webrtc_bin_pad_set_property;
234 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
236 g_object_class_install_property (gobject_class,
237 PROP_PAD_TRANSCEIVER,
238 g_param_spec_object ("transceiver", "Transceiver",
239 "Transceiver associated with this pad",
240 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
241 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
245 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
247 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
248 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
249 gboolean check_negotiation = FALSE;
251 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
254 gst_event_parse_caps (event, &caps);
255 check_negotiation = (!wpad->received_caps
256 || gst_caps_is_equal (wpad->received_caps, caps));
257 gst_caps_replace (&wpad->received_caps, caps);
259 GST_DEBUG_OBJECT (parent,
260 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
261 GST_PTR_FORMAT, pad, check_negotiation, caps);
262 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
263 check_negotiation = TRUE;
266 if (check_negotiation) {
268 _update_need_negotiation (webrtc);
272 return gst_pad_event_default (pad, parent, event);
276 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
280 static GstWebRTCBinPad *
281 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
283 GstWebRTCBinPad *pad =
284 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
287 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
289 if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
290 gst_object_unref (pad);
294 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
295 direction == GST_PAD_SRC ? "src" : "sink");
299 #define gst_webrtc_bin_parent_class parent_class
300 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
301 G_ADD_PRIVATE (GstWebRTCBin)
302 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
303 "webrtcbin element");
306 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
307 GstWebRTCBinPad * pad);
309 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
312 GST_STATIC_CAPS ("application/x-rtp"));
314 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
317 GST_STATIC_CAPS ("application/x-rtp"));
323 CREATE_ANSWER_SIGNAL,
324 SET_LOCAL_DESCRIPTION_SIGNAL,
325 SET_REMOTE_DESCRIPTION_SIGNAL,
326 ADD_ICE_CANDIDATE_SIGNAL,
327 ON_NEGOTIATION_NEEDED_SIGNAL,
328 ON_ICE_CANDIDATE_SIGNAL,
329 ON_NEW_TRANSCEIVER_SIGNAL,
331 ADD_TRANSCEIVER_SIGNAL,
332 GET_TRANSCEIVER_SIGNAL,
333 GET_TRANSCEIVERS_SIGNAL,
334 ADD_TURN_SERVER_SIGNAL,
335 CREATE_DATA_CHANNEL_SIGNAL,
336 ON_DATA_CHANNEL_SIGNAL,
343 PROP_CONNECTION_STATE,
344 PROP_SIGNALING_STATE,
345 PROP_ICE_GATHERING_STATE,
346 PROP_ICE_CONNECTION_STATE,
347 PROP_LOCAL_DESCRIPTION,
348 PROP_CURRENT_LOCAL_DESCRIPTION,
349 PROP_PENDING_LOCAL_DESCRIPTION,
350 PROP_REMOTE_DESCRIPTION,
351 PROP_CURRENT_REMOTE_DESCRIPTION,
352 PROP_PENDING_REMOTE_DESCRIPTION,
356 PROP_ICE_TRANSPORT_POLICY,
359 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
364 GstWebRTCICEStream *stream;
367 /* FIXME: locking? */
369 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
373 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
374 IceStreamItem *item =
375 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
377 if (item->session_id == session_id) {
378 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
379 "session %u", item->stream, session_id);
384 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
390 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
391 GstWebRTCICEStream * stream)
393 IceStreamItem item = { session_id, stream };
395 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
396 "session %u", stream, session_id);
397 g_array_append_val (webrtc->priv->ice_stream_map, item);
407 clear_session_mid_item (SessionMidItem * item)
412 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
415 static GstWebRTCRTPTransceiver *
416 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
417 FindTransceiverFunc func)
421 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
422 GstWebRTCRTPTransceiver *transceiver =
423 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
426 if (func (transceiver, data))
434 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
436 return g_strcmp0 (trans->mid, mid) == 0;
440 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
442 return trans->mline == *mline;
445 static GstWebRTCRTPTransceiver *
446 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
448 GstWebRTCRTPTransceiver *trans;
450 trans = _find_transceiver (webrtc, &mlineindex,
451 (FindTransceiverFunc) transceiver_match_for_mline);
453 GST_TRACE_OBJECT (webrtc,
454 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
460 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
463 static TransportStream *
464 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
465 FindTransportFunc func)
469 for (i = 0; i < webrtc->priv->transports->len; i++) {
470 TransportStream *stream =
471 g_array_index (webrtc->priv->transports, TransportStream *,
474 if (func (stream, data))
482 match_stream_for_session (TransportStream * trans, guint * session)
484 return trans->session_id == *session;
487 static TransportStream *
488 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
490 TransportStream *stream;
492 stream = _find_transport (webrtc, &session_id,
493 (FindTransportFunc) match_stream_for_session);
495 GST_TRACE_OBJECT (webrtc,
496 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
501 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
503 static GstWebRTCBinPad *
504 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
506 GstElement *element = GST_ELEMENT (webrtc);
509 GST_OBJECT_LOCK (webrtc);
511 for (; l; l = g_list_next (l)) {
512 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
514 if (func (l->data, data)) {
515 gst_object_ref (l->data);
516 GST_OBJECT_UNLOCK (webrtc);
521 l = webrtc->priv->pending_pads;
522 for (; l; l = g_list_next (l)) {
523 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
525 if (func (l->data, data)) {
526 gst_object_ref (l->data);
527 GST_OBJECT_UNLOCK (webrtc);
531 GST_OBJECT_UNLOCK (webrtc);
536 typedef gboolean (*FindDataChannelFunc) (GstWebRTCDataChannel * p1,
539 static GstWebRTCDataChannel *
540 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
541 FindDataChannelFunc func)
545 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
546 GstWebRTCDataChannel *channel =
547 g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
550 if (func (channel, data))
558 data_channel_match_for_id (GstWebRTCDataChannel * channel, gint * id)
560 return channel->id == *id;
563 static GstWebRTCDataChannel *
564 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
566 GstWebRTCDataChannel *channel;
568 channel = _find_data_channel (webrtc, &id,
569 (FindDataChannelFunc) data_channel_match_for_id);
571 GST_TRACE_OBJECT (webrtc,
572 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
578 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
580 GST_OBJECT_LOCK (webrtc);
581 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
582 GST_OBJECT_UNLOCK (webrtc);
586 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
588 GST_OBJECT_LOCK (webrtc);
589 webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
590 GST_OBJECT_UNLOCK (webrtc);
594 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
596 _remove_pending_pad (webrtc, pad);
598 if (webrtc->priv->running)
599 gst_pad_set_active (GST_PAD (pad), TRUE);
600 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
604 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
606 _remove_pending_pad (webrtc, pad);
608 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
613 GstPadDirection direction;
618 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
620 return GST_PAD_DIRECTION (pad) == match->direction
621 && pad->mlineindex == match->mlineindex;
624 static GstWebRTCBinPad *
625 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
628 MLineMatch m = { direction, mlineindex };
630 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
635 GstPadDirection direction;
636 GstWebRTCRTPTransceiver *trans;
640 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
642 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
645 static GstWebRTCBinPad *
646 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
647 GstWebRTCRTPTransceiver * trans)
649 TransMatch m = { direction, trans };
651 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
656 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
658 return pad->ssrc == *ssrc;
662 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
669 _unlock_pc_thread (GMutex * lock)
671 g_mutex_unlock (lock);
672 return G_SOURCE_REMOVE;
676 _gst_pc_thread (GstWebRTCBin * webrtc)
679 webrtc->priv->main_context = g_main_context_new ();
680 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
682 PC_COND_BROADCAST (webrtc);
683 g_main_context_invoke (webrtc->priv->main_context,
684 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
686 /* Having the thread be the thread default GMainContext will break the
687 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
689 g_main_loop_run (webrtc->priv->loop);
692 g_main_context_unref (webrtc->priv->main_context);
693 webrtc->priv->main_context = NULL;
694 g_main_loop_unref (webrtc->priv->loop);
695 webrtc->priv->loop = NULL;
696 PC_COND_BROADCAST (webrtc);
703 _start_thread (GstWebRTCBin * webrtc)
706 webrtc->priv->thread = g_thread_new ("gst-pc-ops",
707 (GThreadFunc) _gst_pc_thread, webrtc);
709 while (!webrtc->priv->loop)
710 PC_COND_WAIT (webrtc);
711 webrtc->priv->is_closed = FALSE;
716 _stop_thread (GstWebRTCBin * webrtc)
719 webrtc->priv->is_closed = TRUE;
720 g_main_loop_quit (webrtc->priv->loop);
721 while (webrtc->priv->loop)
722 PC_COND_WAIT (webrtc);
725 g_thread_unref (webrtc->priv->thread);
729 _execute_op (GstWebRTCBinTask * op)
731 PC_LOCK (op->webrtc);
732 if (op->webrtc->priv->is_closed) {
733 GST_DEBUG_OBJECT (op->webrtc,
734 "Peerconnection is closed, aborting execution");
738 op->op (op->webrtc, op->data);
741 PC_UNLOCK (op->webrtc);
742 return G_SOURCE_REMOVE;
746 _free_op (GstWebRTCBinTask * op)
749 op->notify (op->data);
754 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
755 gpointer data, GDestroyNotify notify)
757 GstWebRTCBinTask *op;
760 g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc));
762 if (webrtc->priv->is_closed) {
763 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
768 op = g_new0 (GstWebRTCBinTask, 1);
774 source = g_idle_source_new ();
775 g_source_set_priority (source, G_PRIORITY_DEFAULT);
776 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
777 (GDestroyNotify) _free_op);
778 g_source_attach (source, webrtc->priv->main_context);
779 g_source_unref (source);
782 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
783 static GstWebRTCICEConnectionState
784 _collate_ice_connection_states (GstWebRTCBin * webrtc)
786 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
787 GstWebRTCICEConnectionState any_state = 0;
788 gboolean all_closed = TRUE;
791 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
792 GstWebRTCRTPTransceiver *rtp_trans =
793 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
795 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
796 TransportStream *stream = trans->stream;
797 GstWebRTCICETransport *transport, *rtcp_transport;
798 GstWebRTCICEConnectionState ice_state;
799 gboolean rtcp_mux = FALSE;
801 if (rtp_trans->stopped)
806 g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
808 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
810 /* get transport state */
811 g_object_get (transport, "state", &ice_state, NULL);
812 any_state |= (1 << ice_state);
813 if (ice_state != STATE (CLOSED))
817 webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport;
819 if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) {
820 g_object_get (rtcp_transport, "state", &ice_state, NULL);
821 any_state |= (1 << ice_state);
822 if (ice_state != STATE (CLOSED))
827 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
829 if (webrtc->priv->is_closed) {
830 GST_TRACE_OBJECT (webrtc, "returning closed");
831 return STATE (CLOSED);
833 /* Any of the RTCIceTransport s are in the failed state. */
834 if (any_state & (1 << STATE (FAILED))) {
835 GST_TRACE_OBJECT (webrtc, "returning failed");
836 return STATE (FAILED);
838 /* Any of the RTCIceTransport s are in the disconnected state and
839 * none of them are in the failed state. */
840 if (any_state & (1 << STATE (DISCONNECTED))) {
841 GST_TRACE_OBJECT (webrtc, "returning disconnected");
842 return STATE (DISCONNECTED);
844 /* Any of the RTCIceTransport's are in the checking state and none of them
845 * are in the failed or disconnected state. */
846 if (any_state & (1 << STATE (CHECKING))) {
847 GST_TRACE_OBJECT (webrtc, "returning checking");
848 return STATE (CHECKING);
850 /* Any of the RTCIceTransport s are in the new state and none of them are
851 * in the checking, failed or disconnected state, or all RTCIceTransport's
852 * are in the closed state. */
853 if ((any_state & (1 << STATE (NEW))) || all_closed) {
854 GST_TRACE_OBJECT (webrtc, "returning new");
857 /* All RTCIceTransport s are in the connected, completed or closed state
858 * and at least one of them is in the connected state. */
859 if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 <<
860 STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) {
861 GST_TRACE_OBJECT (webrtc, "returning connected");
862 return STATE (CONNECTED);
864 /* All RTCIceTransport s are in the completed or closed state and at least
865 * one of them is in the completed state. */
866 if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED))
867 && any_state & (1 << STATE (COMPLETED))) {
868 GST_TRACE_OBJECT (webrtc, "returning connected");
869 return STATE (CONNECTED);
872 GST_FIXME ("unspecified situation, returning new");
877 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
878 static GstWebRTCICEGatheringState
879 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
881 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
882 GstWebRTCICEGatheringState any_state = 0;
883 gboolean all_completed = webrtc->priv->transceivers->len > 0;
886 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
887 GstWebRTCRTPTransceiver *rtp_trans =
888 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
890 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
891 TransportStream *stream = trans->stream;
892 GstWebRTCICETransport *transport, *rtcp_transport;
893 GstWebRTCICEGatheringState ice_state;
894 gboolean rtcp_mux = FALSE;
896 if (rtp_trans->stopped)
901 g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
903 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
905 /* get gathering state */
906 g_object_get (transport, "gathering-state", &ice_state, NULL);
907 any_state |= (1 << ice_state);
908 if (ice_state != STATE (COMPLETE))
909 all_completed = FALSE;
912 webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport;
914 if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
915 g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL);
916 any_state |= (1 << ice_state);
917 if (ice_state != STATE (COMPLETE))
918 all_completed = FALSE;
922 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
924 /* Any of the RTCIceTransport s are in the gathering state. */
925 if (any_state & (1 << STATE (GATHERING))) {
926 GST_TRACE_OBJECT (webrtc, "returning gathering");
927 return STATE (GATHERING);
929 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
930 * the completed gathering state. */
932 GST_TRACE_OBJECT (webrtc, "returning complete");
933 return STATE (COMPLETE);
936 /* Any of the RTCIceTransport s are in the new gathering state and none
937 * of the transports are in the gathering state, or there are no transports. */
938 GST_TRACE_OBJECT (webrtc, "returning new");
943 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
944 static GstWebRTCPeerConnectionState
945 _collate_peer_connection_states (GstWebRTCBin * webrtc)
947 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
948 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
949 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
950 GstWebRTCICEConnectionState any_ice_state = 0;
951 GstWebRTCDTLSTransportState any_dtls_state = 0;
954 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
955 GstWebRTCRTPTransceiver *rtp_trans =
956 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
958 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
959 TransportStream *stream = trans->stream;
960 GstWebRTCDTLSTransport *transport, *rtcp_transport;
961 GstWebRTCICEGatheringState ice_state;
962 GstWebRTCDTLSTransportState dtls_state;
963 gboolean rtcp_mux = FALSE;
965 if (rtp_trans->stopped)
970 g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
971 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
973 /* get transport state */
974 g_object_get (transport, "state", &dtls_state, NULL);
975 any_dtls_state |= (1 << dtls_state);
976 g_object_get (transport->transport, "state", &ice_state, NULL);
977 any_ice_state |= (1 << ice_state);
979 rtcp_transport = webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans);
981 if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
982 g_object_get (rtcp_transport, "state", &dtls_state, NULL);
983 any_dtls_state |= (1 << dtls_state);
984 g_object_get (rtcp_transport->transport, "state", &ice_state, NULL);
985 any_ice_state |= (1 << ice_state);
989 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
990 "state: 0x%x", any_ice_state, any_dtls_state);
992 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
993 if (webrtc->priv->is_closed) {
994 GST_TRACE_OBJECT (webrtc, "returning closed");
995 return STATE (CLOSED);
998 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
999 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1000 GST_TRACE_OBJECT (webrtc, "returning failed");
1001 return STATE (FAILED);
1003 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1004 GST_TRACE_OBJECT (webrtc, "returning failed");
1005 return STATE (FAILED);
1008 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting
1009 * or checking state and none of them is in the failed state. */
1010 if (any_ice_state & (1 << ICE_STATE (CHECKING))) {
1011 GST_TRACE_OBJECT (webrtc, "returning connecting");
1012 return STATE (CONNECTING);
1014 if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) {
1015 GST_TRACE_OBJECT (webrtc, "returning connecting");
1016 return STATE (CONNECTING);
1019 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1020 * state and none of them are in the failed or connecting or checking state. */
1021 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1022 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1023 return STATE (DISCONNECTED);
1026 /* All RTCIceTransport's and RTCDtlsTransport's are in the connected,
1027 * completed or closed state and at least of them is in the connected or
1028 * completed state. */
1029 if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 <<
1030 ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED)))
1031 && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 <<
1032 DTLS_STATE (CLOSED)))
1033 && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 <<
1034 ICE_STATE (COMPLETED))
1035 || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) {
1036 GST_TRACE_OBJECT (webrtc, "returning connected");
1037 return STATE (CONNECTED);
1040 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state
1041 * and none of the transports are in the connecting, checking, failed or
1042 * disconnected state, or all transports are in the closed state. */
1043 if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) {
1044 GST_TRACE_OBJECT (webrtc, "returning new");
1047 if ((any_ice_state & (1 << ICE_STATE (NEW))
1048 || any_dtls_state & (1 << DTLS_STATE (NEW)))
1049 && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED)
1050 | (1 << ICE_STATE (DISCONNECTED))))
1051 && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 <<
1052 DTLS_STATE (FAILED)))) {
1053 GST_TRACE_OBJECT (webrtc, "returning new");
1057 GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new");
1065 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1067 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1068 GstWebRTCICEGatheringState new_state;
1070 new_state = _collate_ice_gathering_states (webrtc);
1072 if (new_state != webrtc->ice_gathering_state) {
1073 gchar *old_s, *new_s;
1075 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1077 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1079 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1080 old_s, old_state, new_s, new_state);
1084 webrtc->ice_gathering_state = new_state;
1086 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1092 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1094 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1099 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1101 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1102 GstWebRTCICEConnectionState new_state;
1104 new_state = _collate_ice_connection_states (webrtc);
1106 if (new_state != old_state) {
1107 gchar *old_s, *new_s;
1109 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1111 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1113 GST_INFO_OBJECT (webrtc,
1114 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1119 webrtc->ice_connection_state = new_state;
1121 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1127 _update_ice_connection_state (GstWebRTCBin * webrtc)
1129 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1134 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1136 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1137 GstWebRTCPeerConnectionState new_state;
1139 new_state = _collate_peer_connection_states (webrtc);
1141 if (new_state != old_state) {
1142 gchar *old_s, *new_s;
1144 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1146 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1148 GST_INFO_OBJECT (webrtc,
1149 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1154 webrtc->peer_connection_state = new_state;
1156 g_object_notify (G_OBJECT (webrtc), "connection-state");
1162 _update_peer_connection_state (GstWebRTCBin * webrtc)
1164 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1169 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1172 gboolean res = FALSE;
1174 GST_OBJECT_LOCK (webrtc);
1175 l = GST_ELEMENT (webrtc)->pads;
1176 for (; l; l = g_list_next (l)) {
1177 GstWebRTCBinPad *wpad;
1179 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1182 wpad = GST_WEBRTC_BIN_PAD (l->data);
1183 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1184 && (!wpad->trans || !wpad->trans->stopped)) {
1189 l = webrtc->priv->pending_pads;
1190 for (; l; l = g_list_next (l)) {
1191 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1199 GST_OBJECT_UNLOCK (webrtc);
1203 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1205 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1209 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1211 /* We can't negotiate until we have received caps on all our sink pads,
1212 * as we will need the ssrcs in our offer / answer */
1213 if (!_all_sinks_have_caps (webrtc)) {
1214 GST_LOG_OBJECT (webrtc,
1215 "no negotiation possible until caps have been received on all sink pads");
1219 /* If any implementation-specific negotiation is required, as described at
1220 * the start of this section, return "true".
1222 /* FIXME: emit when input caps/format changes? */
1224 if (!webrtc->current_local_description) {
1225 GST_LOG_OBJECT (webrtc, "no local description set");
1229 if (!webrtc->current_remote_description) {
1230 GST_LOG_OBJECT (webrtc, "no remote description set");
1234 /* If connection has created any RTCDataChannel's, and no m= section has
1235 * been negotiated yet for data, return "true". */
1236 if (webrtc->priv->data_channels->len > 0) {
1237 if (_message_get_datachannel_index (webrtc->current_local_description->
1238 sdp) >= G_MAXUINT) {
1239 GST_LOG_OBJECT (webrtc,
1240 "no data channel media section and have %u " "transports",
1241 webrtc->priv->data_channels->len);
1246 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1247 GstWebRTCRTPTransceiver *trans;
1250 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1253 if (trans->stopped) {
1254 /* FIXME: If t is stopped and is associated with an m= section according to
1255 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1256 * rejected in connection's currentLocalDescription or
1257 * currentRemoteDescription , return "true". */
1258 GST_FIXME_OBJECT (webrtc,
1259 "check if the transceiver is rejected in descriptions");
1261 const GstSDPMedia *media;
1262 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1264 if (trans->mline == -1 || trans->mid == NULL) {
1265 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1266 " mid %s", i, trans, trans->mid);
1269 /* internal inconsistency */
1270 g_assert (trans->mline <
1271 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1272 g_assert (trans->mline <
1273 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1275 /* FIXME: msid handling
1276 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1277 * section in connection's currentLocalDescription doesn't contain an
1278 * "a=msid" line, return "true". */
1281 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1283 local_dir = _get_direction_from_media (media);
1286 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1288 remote_dir = _get_direction_from_media (media);
1290 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1291 /* If connection's currentLocalDescription if of type "offer", and
1292 * the direction of the associated m= section in neither the offer
1293 * nor answer matches t's direction, return "true". */
1295 if (local_dir != trans->direction && remote_dir != trans->direction) {
1296 gchar *local_str, *remote_str, *dir_str;
1299 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1302 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1305 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1308 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1309 "description (local %s remote %s)", dir_str, local_str,
1314 g_free (remote_str);
1318 } else if (webrtc->current_local_description->type ==
1319 GST_WEBRTC_SDP_TYPE_ANSWER) {
1320 GstWebRTCRTPTransceiverDirection intersect_dir;
1322 /* If connection's currentLocalDescription if of type "answer", and
1323 * the direction of the associated m= section in the answer does not
1324 * match t's direction intersected with the offered direction (as
1325 * described in [JSEP] (section 5.3.1.)), return "true". */
1327 /* remote is the offer, local is the answer */
1328 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1330 if (intersect_dir != trans->direction) {
1331 gchar *local_str, *remote_str, *inter_str, *dir_str;
1334 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1337 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1340 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1343 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1346 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1347 "description intersected direction %s (local %s remote %s)",
1348 dir_str, local_str, inter_str, remote_str);
1352 g_free (remote_str);
1361 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1366 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1368 if (webrtc->priv->need_negotiation) {
1369 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1371 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1377 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1379 _update_need_negotiation (GstWebRTCBin * webrtc)
1381 /* If connection's [[isClosed]] slot is true, abort these steps. */
1382 if (webrtc->priv->is_closed)
1384 /* If connection's signaling state is not "stable", abort these steps. */
1385 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1388 /* If the result of checking if negotiation is needed is "false", clear the
1389 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1390 * to false, and abort these steps. */
1391 if (!_check_if_negotiation_is_needed (webrtc)) {
1392 webrtc->priv->need_negotiation = FALSE;
1395 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1396 if (webrtc->priv->need_negotiation)
1398 /* Set connection's [[needNegotiation]] slot to true. */
1399 webrtc->priv->need_negotiation = TRUE;
1400 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1401 * true, fire a simple event named negotiationneeded at connection. */
1402 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1407 _find_codec_preferences (GstWebRTCBin * webrtc,
1408 GstWebRTCRTPTransceiver * rtp_trans, GstPadDirection direction,
1411 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1412 GstCaps *ret = NULL;
1414 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1417 if (rtp_trans && rtp_trans->codec_preferences) {
1418 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1419 rtp_trans->codec_preferences);
1420 ret = gst_caps_ref (rtp_trans->codec_preferences);
1422 GstWebRTCBinPad *pad = NULL;
1424 /* try to find a pad */
1426 || !(pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans)))
1427 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1430 if (trans && trans->last_configured_caps)
1431 ret = gst_caps_ref (trans->last_configured_caps);
1433 GstCaps *caps = NULL;
1435 if (pad->received_caps) {
1436 caps = gst_caps_ref (pad->received_caps);
1437 } else if ((caps = gst_pad_get_current_caps (GST_PAD (pad)))) {
1438 GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1441 if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
1442 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1447 gst_caps_replace (&trans->last_configured_caps, caps);
1452 gst_object_unref (pad);
1457 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1463 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1464 WebRTCTransceiver * trans, const GstCaps * caps)
1469 ret = gst_caps_make_writable (caps);
1471 for (i = 0; i < gst_caps_get_size (ret); i++) {
1472 GstStructure *s = gst_caps_get_structure (ret, i);
1475 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1476 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1478 if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1479 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1480 /* FIXME: is this needed? */
1481 /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1482 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1484 /* FIXME: codec-specific parameters? */
1491 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1492 GParamSpec * pspec, GstWebRTCBin * webrtc)
1494 _update_ice_connection_state (webrtc);
1495 _update_peer_connection_state (webrtc);
1499 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1500 GParamSpec * pspec, GstWebRTCBin * webrtc)
1502 _update_ice_gathering_state (webrtc);
1506 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1507 GParamSpec * pspec, GstWebRTCBin * webrtc)
1509 _update_peer_connection_state (webrtc);
1512 static WebRTCTransceiver *
1513 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
1514 GstWebRTCRTPTransceiverDirection direction, guint mline)
1516 WebRTCTransceiver *trans;
1517 GstWebRTCRTPTransceiver *rtp_trans;
1518 GstWebRTCRTPSender *sender;
1519 GstWebRTCRTPReceiver *receiver;
1521 sender = gst_webrtc_rtp_sender_new ();
1522 receiver = gst_webrtc_rtp_receiver_new ();
1523 trans = webrtc_transceiver_new (webrtc, sender, receiver);
1524 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1525 rtp_trans->direction = direction;
1526 rtp_trans->mline = mline;
1528 g_array_append_val (webrtc->priv->transceivers, trans);
1530 gst_object_unref (sender);
1531 gst_object_unref (receiver);
1533 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
1539 static TransportStream *
1540 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1542 GstWebRTCDTLSTransport *transport;
1543 TransportStream *ret;
1545 /* FIXME: how to parametrize the sender and the receiver */
1546 ret = transport_stream_new (webrtc, session_id);
1547 transport = ret->transport;
1549 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1550 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1551 g_signal_connect (G_OBJECT (transport->transport),
1552 "notify::gathering-state",
1553 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1554 g_signal_connect (G_OBJECT (transport), "notify::state",
1555 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1557 if ((transport = ret->rtcp_transport)) {
1558 g_signal_connect (G_OBJECT (transport->transport),
1559 "notify::state", G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1560 g_signal_connect (G_OBJECT (transport->transport),
1561 "notify::gathering-state",
1562 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1563 g_signal_connect (G_OBJECT (transport), "notify::state",
1564 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1567 GST_TRACE_OBJECT (webrtc,
1568 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1573 static TransportStream *
1574 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1576 TransportStream *ret;
1579 ret = _find_transport_for_session (webrtc, session_id);
1582 ret = _create_transport_channel (webrtc, session_id);
1583 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1584 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1585 g_array_append_val (webrtc->priv->transports, ret);
1587 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1588 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1589 GST_ELEMENT (webrtc->rtpbin), pad_name))
1590 g_warn_if_reached ();
1593 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1594 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1595 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1596 g_warn_if_reached ();
1600 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1601 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1606 /* this is called from the webrtc thread with the pc lock held */
1608 _on_data_channel_ready_state (GstWebRTCDataChannel * channel,
1609 GParamSpec * pspec, GstWebRTCBin * webrtc)
1611 GstWebRTCDataChannelState ready_state;
1614 g_object_get (channel, "ready-state", &ready_state, NULL);
1616 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
1617 gboolean found = FALSE;
1619 for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) {
1620 GstWebRTCDataChannel *c;
1622 c = g_array_index (webrtc->priv->pending_data_channels,
1623 GstWebRTCDataChannel *, i);
1626 g_array_remove_index (webrtc->priv->pending_data_channels, i);
1630 if (found == FALSE) {
1631 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
1635 g_array_append_val (webrtc->priv->data_channels, channel);
1637 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
1638 gst_object_ref (channel));
1643 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
1644 GstWebRTCBin * webrtc)
1646 GstWebRTCDataChannel *channel;
1650 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
1654 channel = _find_data_channel_for_id (webrtc, stream_id);
1656 channel = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, NULL);
1657 channel->id = stream_id;
1658 channel->webrtcbin = webrtc;
1660 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
1661 gst_bin_add (GST_BIN (webrtc), channel->appsink);
1663 gst_element_sync_state_with_parent (channel->appsrc);
1664 gst_element_sync_state_with_parent (channel->appsink);
1666 gst_webrtc_data_channel_link_to_sctp (channel,
1667 webrtc->priv->sctp_transport);
1669 g_array_append_val (webrtc->priv->pending_data_channels, channel);
1672 g_signal_connect (channel, "notify::ready-state",
1673 G_CALLBACK (_on_data_channel_ready_state), webrtc);
1675 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
1676 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
1677 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
1678 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
1679 gst_object_unref (sink_pad);
1684 _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
1685 GstWebRTCBin * webrtc)
1687 GstWebRTCSCTPTransportState state;
1689 g_object_get (sctp, "state", &state, NULL);
1691 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
1695 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
1697 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1698 GstWebRTCDataChannel *channel;
1701 g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
1704 gst_webrtc_data_channel_link_to_sctp (channel,
1705 webrtc->priv->sctp_transport);
1707 if (!channel->negotiated && !channel->opened)
1708 gst_webrtc_data_channel_start_negotiation (channel);
1714 static TransportStream *
1715 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
1717 if (!webrtc->priv->data_channel_transport) {
1718 TransportStream *stream;
1719 GstWebRTCSCTPTransport *sctp_transport;
1722 stream = _find_transport_for_session (webrtc, session_id);
1725 stream = _create_transport_channel (webrtc, session_id);
1726 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
1727 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
1728 g_array_append_val (webrtc->priv->transports, stream);
1731 webrtc->priv->data_channel_transport = stream;
1733 g_object_set (stream, "rtcp-mux", TRUE, NULL);
1735 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
1736 sctp_transport = gst_webrtc_sctp_transport_new ();
1737 sctp_transport->transport =
1738 g_object_ref (webrtc->priv->data_channel_transport->transport);
1739 sctp_transport->webrtcbin = webrtc;
1741 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
1742 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
1745 g_signal_connect (sctp_transport->sctpdec, "pad-added",
1746 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
1747 g_signal_connect (sctp_transport, "notify::state",
1748 G_CALLBACK (_on_sctp_state_notify), webrtc);
1750 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
1751 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
1752 g_warn_if_reached ();
1754 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
1755 GST_ELEMENT (stream->send_bin), "data_sink"))
1756 g_warn_if_reached ();
1758 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1759 GstWebRTCDataChannel *channel;
1762 g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
1765 gst_webrtc_data_channel_link_to_sctp (channel,
1766 webrtc->priv->sctp_transport);
1769 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
1770 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
1772 if (!webrtc->priv->sctp_transport) {
1773 gst_element_sync_state_with_parent (GST_ELEMENT
1774 (sctp_transport->sctpdec));
1775 gst_element_sync_state_with_parent (GST_ELEMENT
1776 (sctp_transport->sctpenc));
1779 webrtc->priv->sctp_transport = sctp_transport;
1782 return webrtc->priv->data_channel_transport;
1785 static TransportStream *
1786 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
1787 gboolean is_datachannel)
1790 return _get_or_create_data_channel_transports (webrtc, session_id);
1792 return _get_or_create_rtp_transport_channel (webrtc, session_id);
1796 g_array_find_uint (GArray * array, guint val)
1800 for (i = 0; i < array->len; i++) {
1801 if (g_array_index (array, guint, i) == val)
1809 _pick_available_pt (GArray * reserved_pts, guint * i)
1811 gboolean ret = FALSE;
1813 for (*i = 96; *i <= 127; (*i)++) {
1814 if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
1815 g_array_append_val (reserved_pts, *i);
1825 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
1826 GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
1827 GstSDPMedia * media)
1829 gboolean ret = TRUE;
1831 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
1834 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
1838 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1841 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
1843 str = g_strdup_printf ("%u", pt);
1844 gst_sdp_media_add_format (media, str);
1846 str = g_strdup_printf ("%u red/%d", pt, clockrate);
1847 gst_sdp_media_add_attribute (media, "rtpmap", str);
1850 *rtx_target_pt = pt;
1852 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1855 str = g_strdup_printf ("%u", pt);
1856 gst_sdp_media_add_format (media, str);
1858 str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
1859 gst_sdp_media_add_attribute (media, "rtpmap", str);
1868 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
1869 GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
1870 GstSDPMedia * media)
1872 gboolean ret = TRUE;
1874 if (trans->local_rtx_ssrc_map)
1875 gst_structure_free (trans->local_rtx_ssrc_map);
1877 trans->local_rtx_ssrc_map =
1878 gst_structure_new_empty ("application/x-rtp-ssrc-map");
1880 if (trans->do_nack) {
1884 if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1887 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
1889 str = g_strdup_printf ("%u", target_ssrc);
1890 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
1891 g_random_int (), NULL);
1894 str = g_strdup_printf ("%u", pt);
1895 gst_sdp_media_add_format (media, str);
1898 str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
1899 gst_sdp_media_add_attribute (media, "rtpmap", str);
1902 str = g_strdup_printf ("%u apt=%d", pt, target_pt);
1903 gst_sdp_media_add_attribute (media, "fmtp", str);
1911 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
1913 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
1914 GstSDPMedia * media)
1919 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
1920 g_value_get_uint (value));
1921 gst_sdp_media_add_attribute (media, "ssrc-group", str);
1931 GstWebRTCBin *webrtc;
1932 WebRTCTransceiver *trans;
1936 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
1942 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
1943 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
1944 cname = gst_structure_get_string (sdes, "cname");
1946 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
1948 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
1949 cname, GST_OBJECT_NAME (data->trans));
1950 gst_sdp_media_add_attribute (data->media, "ssrc", str);
1953 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
1954 gst_sdp_media_add_attribute (data->media, "ssrc", str);
1957 gst_structure_free (sdes);
1963 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
1964 WebRTCTransceiver * trans)
1967 RtxSsrcData data = { media, webrtc, trans };
1971 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
1972 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
1973 cname = gst_structure_get_string (sdes, "cname");
1975 if (trans->local_rtx_ssrc_map)
1976 gst_structure_foreach (trans->local_rtx_ssrc_map,
1977 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
1979 for (i = 0; i < gst_caps_get_size (caps); i++) {
1980 const GstStructure *s = gst_caps_get_structure (caps, i);
1983 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1986 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
1988 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
1989 GST_OBJECT_NAME (trans));
1990 gst_sdp_media_add_attribute (media, "ssrc", str);
1993 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
1994 gst_sdp_media_add_attribute (media, "ssrc", str);
1999 gst_structure_free (sdes);
2001 if (trans->local_rtx_ssrc_map)
2002 gst_structure_foreach (trans->local_rtx_ssrc_map,
2003 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2007 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2008 GstSDPMedia * media)
2010 gchar *cert, *fingerprint, *val;
2012 g_object_get (transport, "certificate", &cert, NULL);
2015 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2018 g_strdup_printf ("%s %s",
2019 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2020 g_free (fingerprint);
2022 gst_sdp_media_add_attribute (media, "fingerprint", val);
2026 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2028 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2029 GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
2030 GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2031 gchar * bundle_pwd, GArray * reserved_pts)
2034 * rtp header extensions
2041 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2043 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2044 gchar *direction, *sdp_mid, *ufrag, *pwd;
2045 gboolean bundle_only;
2049 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2050 || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2053 g_assert (trans->mline == -1 || trans->mline == media_idx);
2055 bundle_only = bundled_mids && bundle_idx != media_idx
2056 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2058 /* mandated by JSEP */
2059 gst_sdp_media_add_attribute (media, "setup", "actpass");
2061 /* FIXME: deal with ICE restarts */
2062 if (last_offer && trans->mline != -1 && trans->mid) {
2063 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2064 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2065 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2067 GST_DEBUG_OBJECT (trans,
2068 "%u Generating new ice parameters mline %i, mid %s", media_idx,
2069 trans->mline, trans->mid);
2070 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2071 _generate_ice_credentials (&ufrag, &pwd);
2073 g_assert (bundle_ufrag && bundle_pwd);
2074 ufrag = g_strdup (bundle_ufrag);
2075 pwd = g_strdup (bundle_pwd);
2079 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2080 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2084 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2085 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2086 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2089 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2092 /* FIXME: negotiate this */
2093 /* FIXME: when bundle_only, these should not be added:
2094 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2095 * However, this causes incompatibilities with current versions
2096 * of the major browsers */
2097 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2098 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2101 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2103 gst_sdp_media_add_attribute (media, direction, "");
2106 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2107 caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
2109 _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
2112 g_assert_not_reached ();
2115 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2116 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2118 gst_caps_unref (caps);
2122 for (i = 0; i < gst_caps_get_size (caps); i++) {
2123 GstCaps *format = gst_caps_new_empty ();
2124 const GstStructure *s = gst_caps_get_structure (caps, i);
2126 gst_caps_append_structure (format, gst_structure_copy (s));
2128 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
2129 " to %u-th media", i, format, media_idx);
2131 /* this only looks at the first structure so we loop over the given caps
2132 * and add each structure inside it piecemeal */
2133 gst_sdp_media_set_media_from_caps (format, media);
2135 gst_caps_unref (format);
2138 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2139 const GstStructure *s = gst_caps_get_structure (caps, 0);
2140 gint clockrate = -1;
2142 gint original_rtx_target_pt; /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2143 guint rtx_target_ssrc = -1;
2145 if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
2146 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2147 g_array_append_val (reserved_pts, rtx_target_pt);
2149 original_rtx_target_pt = rtx_target_pt;
2151 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
2152 GST_WARNING_OBJECT (webrtc,
2153 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
2154 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
2155 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
2158 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2159 clockrate, &rtx_target_pt, media);
2160 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2161 clockrate, rtx_target_pt, rtx_target_ssrc, media);
2162 if (original_rtx_target_pt != rtx_target_pt)
2163 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2164 clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
2167 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
2169 /* Some identifier; we also add the media name to it so it's identifiable */
2171 gst_sdp_media_add_attribute (media, "mid", trans->mid);
2173 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2174 webrtc->priv->media_counter++);
2175 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2180 * - add a=candidate lines for gathered candidates
2183 if (trans->sender) {
2184 if (!trans->sender->transport) {
2185 TransportStream *item;
2188 _get_or_create_transport_stream (webrtc,
2189 bundled_mids ? bundle_idx : media_idx, FALSE);
2191 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
2194 _add_fingerprint_to_media (trans->sender->transport, media);
2198 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2201 g_string_append_printf (bundled_mids, " %s", mid);
2204 gst_caps_unref (caps);
2210 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
2212 if (pad->received_caps) {
2213 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
2216 if (gst_structure_get_int (s, "payload", &pt)) {
2217 g_array_append_val (reserved_pts, pt);
2223 gather_reserved_pts (GstWebRTCBin * webrtc)
2225 GstElement *element = GST_ELEMENT (webrtc);
2226 GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2228 GST_OBJECT_LOCK (webrtc);
2229 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
2230 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
2232 GST_OBJECT_UNLOCK (webrtc);
2234 return reserved_pts;
2238 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
2239 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
2240 gchar * bundle_ufrag, gchar * bundle_pwd)
2242 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2243 gchar *ufrag, *pwd, *sdp_mid;
2244 gboolean bundle_only = bundled_mids
2245 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2246 && gst_sdp_message_medias_len (msg) != bundle_idx;
2247 guint last_data_index = G_MAXUINT;
2249 /* add data channel support */
2250 if (webrtc->priv->data_channels->len == 0)
2254 last_data_index = _message_get_datachannel_index (last_offer);
2255 if (last_data_index < G_MAXUINT) {
2256 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
2257 /* XXX: is this always true when recycling transceivers?
2258 * i.e. do we always put the data channel in the same mline */
2259 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
2263 /* mandated by JSEP */
2264 gst_sdp_media_add_attribute (media, "setup", "actpass");
2266 /* FIXME: only needed when restarting ICE */
2267 if (last_offer && last_data_index < G_MAXUINT) {
2268 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
2269 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
2271 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2272 _generate_ice_credentials (&ufrag, &pwd);
2274 ufrag = g_strdup (bundle_ufrag);
2275 pwd = g_strdup (bundle_pwd);
2278 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2279 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2283 gst_sdp_media_set_media (media, "application");
2284 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
2285 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2286 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2287 gst_sdp_media_add_format (media, "webrtc-datachannel");
2289 if (bundle_idx != gst_sdp_message_medias_len (msg))
2290 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2292 if (last_offer && last_data_index < G_MAXUINT) {
2293 const GstSDPMedia *last_data_media;
2296 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
2297 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
2299 gst_sdp_media_add_attribute (media, "mid", mid);
2301 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2302 webrtc->priv->media_counter++);
2303 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2308 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2311 g_string_append_printf (bundled_mids, " %s", mid);
2314 /* FIXME: negotiate this properly */
2315 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2317 _get_or_create_data_channel_transports (webrtc,
2318 bundled_mids ? 0 : webrtc->priv->transceivers->len);
2319 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
2324 /* TODO: use the options argument */
2325 static GstSDPMessage *
2326 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
2329 GString *bundled_mids = NULL;
2330 gchar *bundle_ufrag = NULL;
2331 gchar *bundle_pwd = NULL;
2332 GArray *reserved_pts = NULL;
2333 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2334 GList *seen_transceivers = NULL;
2335 guint media_idx = 0;
2338 gst_sdp_message_new (&ret);
2340 gst_sdp_message_set_version (ret, "0");
2343 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
2345 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
2346 sess_id = g_strdup (origin->sess_id);
2348 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
2350 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
2354 gst_sdp_message_set_session_name (ret, "-");
2355 gst_sdp_message_add_time (ret, "0", "0", NULL);
2356 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
2358 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
2359 bundled_mids = g_string_new ("BUNDLE");
2360 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
2361 bundled_mids = g_string_new ("BUNDLE");
2364 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2365 GStrv last_bundle = NULL;
2366 guint bundle_media_index;
2368 reserved_pts = gather_reserved_pts (webrtc);
2369 if (last_offer && _parse_bundle (last_offer, &last_bundle) && last_bundle
2370 && last_bundle && last_bundle[0]
2371 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
2373 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
2375 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
2377 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2380 g_strfreev (last_bundle);
2383 /* FIXME: recycle transceivers */
2385 /* Fill up the renegotiated streams first */
2387 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
2388 GstWebRTCRTPTransceiver *trans = NULL;
2389 const GstSDPMedia *last_media;
2391 last_media = gst_sdp_message_get_media (last_offer, i);
2393 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
2394 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
2395 const gchar *last_mid;
2397 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
2399 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2401 g_array_index (webrtc->priv->transceivers,
2402 GstWebRTCRTPTransceiver *, j);
2404 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
2407 g_assert (!g_list_find (seen_transceivers, trans));
2409 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
2410 GST_PTR_FORMAT " with mid %s into media index %u", trans,
2411 trans->mid, media_idx);
2413 /* FIXME: deal with format changes */
2414 gst_sdp_media_copy (last_media, &media);
2415 _media_replace_direction (media, trans->direction);
2418 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2421 g_string_append_printf (bundled_mids, " %s", mid);
2424 gst_sdp_message_add_media (ret, media);
2427 gst_sdp_media_free (media);
2428 seen_transceivers = g_list_prepend (seen_transceivers, trans);
2432 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
2433 "application") == 0) {
2434 GstSDPMedia media = { 0, };
2435 gst_sdp_media_init (&media);
2436 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
2437 bundle_ufrag, bundle_pwd)) {
2438 gst_sdp_message_add_media (ret, &media);
2441 gst_sdp_media_uninit (&media);
2447 /* add any extra streams */
2448 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2449 GstWebRTCRTPTransceiver *trans;
2450 GstSDPMedia media = { 0, };
2453 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
2456 /* don't add transceivers twice */
2457 if (g_list_find (seen_transceivers, trans))
2460 /* don't add stopped transceivers */
2464 gst_sdp_media_init (&media);
2466 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2467 reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2470 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
2471 "index %u", trans, media_idx);
2473 if (sdp_media_from_transceiver (webrtc, &media, trans,
2474 GST_WEBRTC_SDP_TYPE_OFFER, media_idx, bundled_mids, 0, bundle_ufrag,
2475 bundle_pwd, reserved_pts)) {
2476 gst_sdp_message_add_media (ret, &media);
2479 gst_sdp_media_uninit (&media);
2482 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2483 g_array_free (reserved_pts, TRUE);
2485 seen_transceivers = g_list_prepend (seen_transceivers, trans);
2488 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2489 g_array_free (reserved_pts, TRUE);
2492 /* add a data channel if exists and not renegotiated */
2493 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
2494 GstSDPMedia media = { 0, };
2495 gst_sdp_media_init (&media);
2496 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
2497 bundle_ufrag, bundle_pwd)) {
2498 gst_sdp_message_add_media (ret, &media);
2501 gst_sdp_media_uninit (&media);
2505 g_assert (media_idx == gst_sdp_message_medias_len (ret));
2508 gchar *mids = g_string_free (bundled_mids, FALSE);
2510 gst_sdp_message_add_attribute (ret, "group", mids);
2515 g_free (bundle_ufrag);
2518 g_free (bundle_pwd);
2520 /* FIXME: pre-emptively setup receiving elements when needed */
2522 /* XXX: only true for the initial offerer */
2523 g_object_set (webrtc->priv->ice, "controller", TRUE, NULL);
2525 g_list_free (seen_transceivers);
2527 if (webrtc->priv->last_generated_answer)
2528 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
2529 webrtc->priv->last_generated_answer = NULL;
2530 if (webrtc->priv->last_generated_offer)
2531 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
2533 GstSDPMessage *copy;
2534 gst_sdp_message_copy (ret, ©);
2535 webrtc->priv->last_generated_offer =
2536 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
2543 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
2544 gint * rtx_target_pt)
2548 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2551 for (i = 0; i < gst_caps_get_size (caps); i++) {
2552 const GstStructure *s = gst_caps_get_structure (caps, i);
2554 if (gst_structure_has_name (s, "application/x-rtp")) {
2555 const gchar *encoding_name =
2556 gst_structure_get_string (s, "encoding-name");
2560 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
2561 gst_structure_get_int (s, "payload", &pt)) {
2562 if (!g_strcmp0 (encoding_name, "RED")) {
2565 str = g_strdup_printf ("%u", pt);
2566 gst_sdp_media_add_format (media, str);
2568 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
2569 *rtx_target_pt = pt;
2570 gst_sdp_media_add_attribute (media, "rtpmap", str);
2572 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
2575 str = g_strdup_printf ("%u", pt);
2576 gst_sdp_media_add_format (media, str);
2578 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
2579 gst_sdp_media_add_attribute (media, "rtpmap", str);
2588 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
2589 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
2592 const GstStructure *s;
2594 if (trans->local_rtx_ssrc_map)
2595 gst_structure_free (trans->local_rtx_ssrc_map);
2597 trans->local_rtx_ssrc_map =
2598 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2600 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
2601 s = gst_caps_get_structure (offer_caps, i);
2603 if (gst_structure_has_name (s, "application/x-rtp")) {
2604 const gchar *encoding_name =
2605 gst_structure_get_string (s, "encoding-name");
2606 const gchar *apt_str = gst_structure_get_string (s, "apt");
2614 apt = atoi (apt_str);
2616 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
2617 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
2618 if (!g_strcmp0 (encoding_name, "RTX")) {
2621 str = g_strdup_printf ("%u", pt);
2622 gst_sdp_media_add_format (media, str);
2624 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
2625 gst_sdp_media_add_attribute (media, "rtpmap", str);
2628 str = g_strdup_printf ("%d apt=%d", pt, apt);
2629 gst_sdp_media_add_attribute (media, "fmtp", str);
2632 str = g_strdup_printf ("%u", target_ssrc);
2633 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2634 g_random_int (), NULL);
2642 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
2643 guint * target_ssrc)
2645 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
2647 gst_structure_get_int (s, "payload", target_pt);
2648 gst_structure_get_uint (s, "ssrc", target_ssrc);
2651 /* TODO: use the options argument */
2652 static GstSDPMessage *
2653 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
2655 GstSDPMessage *ret = NULL;
2656 const GstWebRTCSessionDescription *pending_remote =
2657 webrtc->pending_remote_description;
2659 GStrv bundled = NULL;
2660 guint bundle_idx = 0;
2661 GString *bundled_mids = NULL;
2662 gchar *bundle_ufrag = NULL;
2663 gchar *bundle_pwd = NULL;
2664 GList *seen_transceivers = NULL;
2665 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
2667 if (!webrtc->pending_remote_description) {
2668 GST_ERROR_OBJECT (webrtc,
2669 "Asked to create an answer without a remote description");
2673 if (!_parse_bundle (pending_remote->sdp, &bundled))
2677 GStrv last_bundle = NULL;
2678 guint bundle_media_index;
2680 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
2681 GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
2686 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2687 bundled_mids = g_string_new ("BUNDLE");
2690 if (last_answer && _parse_bundle (last_answer, &last_bundle)
2691 && last_bundle && last_bundle[0]
2692 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
2694 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
2696 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
2698 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2701 g_strfreev (last_bundle);
2704 gst_sdp_message_new (&ret);
2706 gst_sdp_message_set_version (ret, "0");
2708 const GstSDPOrigin *offer_origin =
2709 gst_sdp_message_get_origin (pending_remote->sdp);
2710 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
2711 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
2713 gst_sdp_message_set_session_name (ret, "-");
2715 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
2716 const GstSDPAttribute *attr =
2717 gst_sdp_message_get_attribute (pending_remote->sdp, i);
2719 if (g_strcmp0 (attr->key, "ice-options") == 0) {
2720 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
2724 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
2725 GstSDPMedia *media = NULL;
2726 GstSDPMedia *offer_media;
2727 GstWebRTCDTLSSetup offer_setup, answer_setup;
2729 gboolean bundle_only;
2733 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
2734 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
2736 gst_sdp_media_new (&media);
2737 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2738 gst_sdp_media_set_port_info (media, 0, 0);
2740 gst_sdp_media_set_port_info (media, 9, 0);
2741 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2746 /* FIXME: deal with ICE restarts */
2747 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
2748 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
2749 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
2752 _generate_ice_credentials (&ufrag, &pwd);
2754 ufrag = g_strdup (bundle_ufrag);
2755 pwd = g_strdup (bundle_pwd);
2758 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2759 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2764 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
2765 const GstSDPAttribute *attr =
2766 gst_sdp_media_get_attribute (offer_media, j);
2768 if (g_strcmp0 (attr->key, "mid") == 0
2769 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
2770 gst_sdp_media_add_attribute (media, attr->key, attr->value);
2771 /* FIXME: handle anything we want to keep */
2775 mid = gst_sdp_media_get_attribute_val (media, "mid");
2776 /* XXX: not strictly required but a lot of functionality requires a mid */
2779 /* set the a=setup: attribute */
2780 offer_setup = _get_dtls_setup_from_media (offer_media);
2781 answer_setup = _intersect_dtls_setup (offer_setup);
2782 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
2783 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
2784 "transceiver direction");
2787 _media_replace_setup (media, answer_setup);
2789 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
2792 if (gst_sdp_media_formats_len (offer_media) != 1) {
2793 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
2794 "for webrtc-datachannel");
2797 sctp_port = _get_sctp_port_from_media (offer_media);
2798 if (sctp_port == -1) {
2799 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
2803 /* XXX: older browsers will produce a different SDP format for data
2804 * channel that is currently not parsed correctly */
2805 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2807 gst_sdp_media_set_media (media, "application");
2808 gst_sdp_media_set_port_info (media, 9, 0);
2809 gst_sdp_media_add_format (media, "webrtc-datachannel");
2811 /* FIXME: negotiate this properly on renegotiation */
2812 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2814 _get_or_create_data_channel_transports (webrtc,
2815 bundled_mids ? bundle_idx : i);
2819 g_string_append_printf (bundled_mids, " %s", mid);
2822 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
2824 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
2825 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
2826 GstCaps *offer_caps, *answer_caps = NULL;
2827 GstWebRTCRTPTransceiver *rtp_trans = NULL;
2828 WebRTCTransceiver *trans = NULL;
2829 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
2830 gint target_pt = -1;
2831 gint original_target_pt = -1;
2832 guint target_ssrc = 0;
2834 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2835 offer_caps = _rtp_caps_from_media (offer_media);
2837 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
2839 _find_transceiver (webrtc, mid,
2840 (FindTransceiverFunc) match_for_mid))) {
2841 const GstSDPMedia *last_media =
2842 gst_sdp_message_get_media (last_answer, i);
2843 const gchar *last_mid =
2844 gst_sdp_media_get_attribute_val (last_media, "mid");
2846 /* FIXME: assumes no shenanigans with recycling transceivers */
2847 g_assert (g_strcmp0 (mid, last_mid) == 0);
2850 && (rtp_trans->direction ==
2851 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
2852 || rtp_trans->direction ==
2853 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY))
2855 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
2857 && (rtp_trans->direction ==
2858 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
2859 || rtp_trans->direction ==
2860 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY))
2862 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SRC, i);
2864 answer_caps = _rtp_caps_from_media (last_media);
2866 /* XXX: In theory we're meant to use the sendrecv formats for the
2867 * inactive direction however we don't know what that may be and would
2868 * require asking outside what it expects to possibly send later */
2870 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
2871 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
2872 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
2874 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2875 GstCaps *trans_caps;
2878 g_array_index (webrtc->priv->transceivers,
2879 GstWebRTCRTPTransceiver *, j);
2881 if (g_list_find (seen_transceivers, rtp_trans)) {
2882 /* Don't double allocate a transceiver to multiple mlines */
2888 _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
2890 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
2891 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
2893 /* FIXME: technically this is a little overreaching as some fields we
2894 * we can deal with not having and/or we may have unrecognized fields
2895 * that we cannot actually support */
2897 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
2898 if (answer_caps && !gst_caps_is_empty (answer_caps)) {
2899 GST_LOG_OBJECT (webrtc,
2900 "found compatible transceiver %" GST_PTR_FORMAT
2901 " for offer media %u", rtp_trans, i);
2903 gst_caps_unref (trans_caps);
2907 gst_caps_unref (answer_caps);
2911 gst_caps_unref (trans_caps);
2921 answer_dir = rtp_trans->direction;
2922 g_assert (answer_caps != NULL);
2924 /* if no transceiver, then we only receive that stream and respond with
2925 * the exact same caps */
2926 /* FIXME: how to validate that subsequent elements can actually receive
2927 * this payload/format */
2928 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
2929 answer_caps = gst_caps_ref (offer_caps);
2932 if (gst_caps_is_empty (answer_caps)) {
2933 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
2935 gst_object_unref (rtp_trans);
2936 gst_caps_unref (answer_caps);
2940 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
2943 trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
2944 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2946 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
2947 " for mline %u", trans, i);
2949 trans = WEBRTC_TRANSCEIVER (rtp_trans);
2952 if (!trans->do_nack) {
2953 answer_caps = gst_caps_make_writable (answer_caps);
2954 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
2955 GstStructure *s = gst_caps_get_structure (answer_caps, k);
2956 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
2960 gst_sdp_media_set_media_from_caps (answer_caps, media);
2962 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
2965 original_target_pt = target_pt;
2967 _media_add_fec (media, trans, offer_caps, &target_pt);
2968 if (trans->do_nack) {
2969 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
2970 if (target_pt != original_target_pt)
2971 _media_add_rtx (media, trans, offer_caps, original_target_pt,
2975 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
2976 _media_add_ssrcs (media, answer_caps, webrtc,
2977 WEBRTC_TRANSCEIVER (rtp_trans));
2979 gst_caps_unref (answer_caps);
2982 /* set the new media direction */
2983 offer_dir = _get_direction_from_media (offer_media);
2984 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
2985 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
2986 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
2987 "transceiver direction");
2990 _media_replace_direction (media, answer_dir);
2992 if (!trans->stream) {
2993 TransportStream *item;
2996 _get_or_create_transport_stream (webrtc,
2997 bundled_mids ? bundle_idx : i, FALSE);
2998 webrtc_transceiver_set_transport (trans, item);
3002 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3005 g_string_append_printf (bundled_mids, " %s", mid);
3008 /* set the a=fingerprint: for this transport */
3009 _add_fingerprint_to_media (trans->stream->transport, media);
3011 gst_caps_unref (offer_caps);
3013 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
3019 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
3020 gst_sdp_media_free (media);
3021 gst_sdp_media_copy (offer_media, &media);
3022 gst_sdp_media_set_port_info (media, 0, 0);
3024 gst_sdp_message_add_media (ret, media);
3025 gst_sdp_media_free (media);
3029 gchar *mids = g_string_free (bundled_mids, FALSE);
3031 gst_sdp_message_add_attribute (ret, "group", mids);
3036 g_free (bundle_ufrag);
3039 g_free (bundle_pwd);
3041 /* FIXME: can we add not matched transceivers? */
3043 /* XXX: only true for the initial offerer */
3044 g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
3047 g_strfreev (bundled);
3049 g_list_free (seen_transceivers);
3051 if (webrtc->priv->last_generated_offer)
3052 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3053 webrtc->priv->last_generated_offer = NULL;
3054 if (webrtc->priv->last_generated_answer)
3055 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3057 GstSDPMessage *copy;
3058 gst_sdp_message_copy (ret, ©);
3059 webrtc->priv->last_generated_answer =
3060 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
3068 GstStructure *options;
3069 GstPromise *promise;
3070 GstWebRTCSDPType type;
3074 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
3076 GstWebRTCSessionDescription *desc = NULL;
3077 GstSDPMessage *sdp = NULL;
3078 GstStructure *s = NULL;
3080 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
3081 gst_webrtc_sdp_type_to_string (data->type), data->options);
3083 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
3084 sdp = _create_offer_task (webrtc, data->options);
3085 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
3086 sdp = _create_answer_task (webrtc, data->options);
3088 g_assert_not_reached ();
3093 desc = gst_webrtc_session_description_new (data->type, sdp);
3094 s = gst_structure_new ("application/x-gst-promise",
3095 gst_webrtc_sdp_type_to_string (data->type),
3096 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
3101 gst_promise_reply (data->promise, s);
3105 gst_webrtc_session_description_free (desc);
3109 _free_create_sdp_data (struct create_sdp *data)
3112 gst_structure_free (data->options);
3113 gst_promise_unref (data->promise);
3118 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
3119 const GstStructure * options, GstPromise * promise)
3121 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3124 data->options = gst_structure_copy (options);
3125 data->promise = gst_promise_ref (promise);
3126 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
3128 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3129 data, (GDestroyNotify) _free_create_sdp_data);
3133 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
3134 const GstStructure * options, GstPromise * promise)
3136 struct create_sdp *data = g_new0 (struct create_sdp, 1);
3139 data->options = gst_structure_copy (options);
3140 data->promise = gst_promise_ref (promise);
3141 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
3143 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3144 data, (GDestroyNotify) _free_create_sdp_data);
3147 static GstWebRTCBinPad *
3148 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
3151 GstWebRTCBinPad *pad;
3155 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
3157 pad = gst_webrtc_bin_pad_new (pad_name, direction);
3159 pad->mlineindex = media_idx;
3164 static GstWebRTCRTPTransceiver *
3165 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
3166 const GstSDPMessage * sdp, guint media_idx)
3168 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3169 GstWebRTCRTPTransceiver *ret = NULL;
3172 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3173 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3175 if (g_strcmp0 (attr->key, "mid") == 0) {
3177 _find_transceiver (webrtc, attr->value,
3178 (FindTransceiverFunc) match_for_mid)))
3183 ret = _find_transceiver (webrtc, &media_idx,
3184 (FindTransceiverFunc) transceiver_match_for_mline);
3187 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
3192 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
3197 * ,-------------------------webrtcbin-------------------------,
3199 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3200 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3202 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3203 * ; sink_%u ; ; '---------------------' ;
3204 * o----------o send_rtp_sink_%u ; ;
3205 * ; '--------------------' ;
3206 * '--------------------- -------------------------------------'
3211 * ,--------------------------------webrtcbin--------------------------------,
3213 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
3214 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
3216 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
3217 * ; sink_%u ,---funnel---, ; ; '---------------------' ;
3218 * o---------o sink_%u ; ; ; ;
3219 * ; sink_%u ; src o-o send_rtp_sink_%u ; ;
3220 * o---------o sink_%u ; ; ; ;
3221 * ; '------------' '--------------------' ;
3222 * '-------------------------------------------------------------------------'
3224 GstPadTemplate *rtp_templ;
3227 WebRTCTransceiver *trans;
3229 g_return_val_if_fail (pad->trans != NULL, NULL);
3231 GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
3233 trans = WEBRTC_TRANSCEIVER (pad->trans);
3235 g_assert (trans->stream);
3237 if (!webrtc->rtpfunnel) {
3239 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
3240 "send_rtp_sink_%u");
3241 g_assert (rtp_templ);
3243 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
3245 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
3247 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
3248 gst_object_unref (rtp_sink);
3250 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
3251 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3252 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
3253 g_warn_if_reached ();
3256 gchar *pad_name = g_strdup_printf ("sink_%u", pad->mlineindex);
3257 GstPad *funnel_sinkpad =
3258 gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
3260 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
3263 gst_object_unref (funnel_sinkpad);
3266 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
3268 return GST_PAD (pad);
3271 /* output pads are receiving elements */
3273 _connect_output_stream (GstWebRTCBin * webrtc,
3274 TransportStream * stream, guint session_id)
3277 * ,------------------------webrtcbin------------------------,
3278 * ; ,---------rtpbin---------, ;
3279 * ; ,-transport_receive_%u--, ; ; ;
3280 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
3282 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
3283 * ; '-----------------------' ; ; ; src_%u
3284 * ; ; recv_rtp_src_%u_%u_%u o--o
3285 * ; '------------------------' ;
3286 * '---------------------------------------------------------'
3290 if (stream->output_connected) {
3291 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
3292 "connected to rtpbin. Not connecting", stream);
3296 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
3297 session_id, stream);
3299 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
3300 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
3301 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
3302 g_warn_if_reached ();
3305 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
3307 /* The webrtcbin src_%u output pads will be created when rtpbin receives
3308 * data on that stream in on_rtpbin_pad_added() */
3310 stream->output_connected = TRUE;
3320 _clear_ice_candidate_item (IceCandidateItem ** item)
3322 g_free ((*item)->candidate);
3327 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
3328 gboolean drop_invalid)
3330 GstWebRTCICEStream *stream;
3332 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
3333 if (stream == NULL) {
3335 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
3338 IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
3339 new->mlineindex = item->mlineindex;
3340 new->candidate = g_strdup (item->candidate);
3342 g_array_append_val (webrtc->priv->pending_ice_candidates, new);
3343 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
3348 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
3349 item->mlineindex, item->candidate);
3351 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
3355 _filter_sdp_fields (GQuark field_id, const GValue * value,
3356 GstStructure * new_structure)
3358 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
3359 gst_structure_id_set_value (new_structure, field_id, value);
3365 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
3370 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
3371 GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
3373 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
3376 for (i = 0; i < rtx_count; i++) {
3377 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
3378 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
3379 const gchar *apt = gst_structure_get_string (s, "apt");
3381 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
3382 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
3385 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
3386 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
3387 stream->rtxsend, pt_map);
3389 if (stream->rtxreceive)
3390 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
3391 if (stream->rtxsend)
3392 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
3397 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
3398 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
3402 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3405 proto = gst_sdp_media_get_proto (media);
3406 if (proto != NULL) {
3407 /* Parse global SDP attributes once */
3408 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
3409 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
3410 gst_sdp_message_attributes_to_caps (sdp, global_caps);
3411 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
3412 gst_sdp_media_attributes_to_caps (media, global_caps);
3414 len = gst_sdp_media_formats_len (media);
3415 for (i = 0; i < len; i++) {
3416 GstCaps *caps, *outcaps;
3422 pt = atoi (gst_sdp_media_get_format (media, i));
3424 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
3427 caps = gst_sdp_media_get_caps_from_media (media, pt);
3429 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
3433 /* Merge in global caps */
3434 /* Intersect will merge in missing fields to the current caps */
3435 outcaps = gst_caps_intersect (caps, global_caps);
3436 gst_caps_unref (caps);
3438 s = gst_caps_get_structure (outcaps, 0);
3439 gst_structure_set_name (s, "application/x-rtp");
3440 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
3441 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
3443 item.caps = gst_caps_new_empty ();
3445 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
3446 GstStructure *s = gst_caps_get_structure (outcaps, j);
3447 GstStructure *filtered =
3448 gst_structure_new_empty (gst_structure_get_name (s));
3450 gst_structure_foreach (s,
3451 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
3452 gst_caps_append_structure (item.caps, filtered);
3456 gst_caps_unref (outcaps);
3458 g_array_append_val (stream->ptmap, item);
3461 gst_caps_unref (global_caps);
3466 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
3467 const GstSDPMessage * sdp, guint media_idx,
3468 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
3469 GStrv bundled, guint bundle_idx)
3471 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
3472 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
3473 GstWebRTCRTPTransceiverDirection new_dir;
3474 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3475 GstWebRTCDTLSSetup new_setup;
3476 gboolean new_rtcp_mux, new_rtcp_rsize;
3477 ReceiveState receive_state = 0;
3480 rtp_trans->mline = media_idx;
3482 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3483 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3485 if (g_strcmp0 (attr->key, "mid") == 0) {
3486 g_free (rtp_trans->mid);
3487 rtp_trans->mid = g_strdup (attr->value);
3492 const GstSDPMedia *local_media, *remote_media;
3493 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
3494 GstWebRTCDTLSSetup local_setup, remote_setup;
3497 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
3500 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
3503 local_setup = _get_dtls_setup_from_media (local_media);
3504 remote_setup = _get_dtls_setup_from_media (remote_media);
3505 new_setup = _get_final_setup (local_setup, remote_setup);
3506 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
3509 local_dir = _get_direction_from_media (local_media);
3510 remote_dir = _get_direction_from_media (remote_media);
3511 new_dir = _get_final_direction (local_dir, remote_dir);
3513 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3516 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
3517 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
3518 && prev_dir != new_dir) {
3519 GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
3523 if (!bundled || bundle_idx == media_idx) {
3524 new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
3525 && _media_has_attribute_key (remote_media, "rtcp-mux");
3526 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
3527 && _media_has_attribute_key (remote_media, "rtcp-rsize");
3531 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
3532 media_idx, &session);
3534 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
3535 g_object_unref (session);
3539 g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
3543 if (new_dir != prev_dir) {
3544 GST_TRACE_OBJECT (webrtc, "transceiver direction change");
3546 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
3547 GstWebRTCBinPad *pad;
3549 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
3551 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
3553 GstPad *peer = gst_pad_get_peer (target);
3555 gst_pad_send_event (peer, gst_event_new_eos ());
3556 gst_object_unref (peer);
3558 gst_object_unref (target);
3560 gst_object_unref (pad);
3563 /* XXX: send eos event up the sink pad as well? */
3566 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
3567 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
3568 GstWebRTCBinPad *pad =
3569 _find_pad_for_mline (webrtc, GST_PAD_SINK, media_idx);
3571 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
3572 " for transceiver %" GST_PTR_FORMAT, pad, trans);
3573 g_assert (pad->trans == rtp_trans);
3574 g_assert (pad->mlineindex == media_idx);
3575 gst_object_unref (pad);
3577 GST_DEBUG_OBJECT (webrtc,
3578 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
3579 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, media_idx);
3580 pad->trans = gst_object_ref (rtp_trans);
3581 _connect_input_stream (webrtc, pad);
3582 _add_pad (webrtc, pad);
3585 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
3586 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
3587 GstWebRTCBinPad *pad =
3588 _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
3590 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
3591 " for transceiver %" GST_PTR_FORMAT, pad, trans);
3592 g_assert (pad->trans == rtp_trans);
3593 g_assert (pad->mlineindex == media_idx);
3594 gst_object_unref (pad);
3596 GST_DEBUG_OBJECT (webrtc,
3597 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
3598 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
3599 pad->trans = gst_object_ref (rtp_trans);
3601 if (!trans->stream) {
3602 TransportStream *item;
3605 _get_or_create_transport_stream (webrtc,
3606 bundled ? bundle_idx : media_idx, FALSE);
3607 webrtc_transceiver_set_transport (trans, item);
3610 _connect_output_stream (webrtc, trans->stream,
3611 bundled ? bundle_idx : media_idx);
3612 /* delay adding the pad until rtpbin creates the recv output pad
3613 * to ghost to so queries/events travel through the pipeline correctly
3614 * as soon as the pad is added */
3615 _add_pad_to_list (webrtc, pad);
3618 receive_state = RECEIVE_STATE_PASS;
3619 } else if (!bundled) {
3620 receive_state = RECEIVE_STATE_DROP;
3623 rtp_trans->mline = media_idx;
3624 rtp_trans->current_direction = new_dir;
3627 if (!bundled || bundle_idx == media_idx) {
3628 if (stream->rtxsend || stream->rtxreceive) {
3629 _set_rtx_ptmap_from_stream (webrtc, stream);
3632 g_object_set (stream, "dtls-client",
3633 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
3636 /* Must be after setting the "dtls-client" so that data is not pushed into
3637 * the dtlssrtp elements before the ssl direction has been set which will
3638 * throw SSL errors */
3639 if (receive_state > 0)
3640 transport_receive_bin_set_receive_state (stream->receive_bin,
3644 /* must be called with the pc lock held */
3646 _generate_data_channel_id (GstWebRTCBin * webrtc)
3649 gint new_id = -1, max_channels = 0;
3651 if (webrtc->priv->sctp_transport) {
3652 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
3655 if (max_channels <= 0) {
3656 max_channels = 65534;
3659 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
3662 /* TODO: a better search algorithm */
3664 GstWebRTCDataChannel *channel;
3668 if (new_id < 0 || new_id >= max_channels) {
3669 /* exhausted id space */
3670 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
3671 "data channel id (max %i)", max_channels);
3675 /* client must generate even ids, server must generate odd ids */
3676 if (new_id % 2 == ! !is_client)
3679 channel = _find_data_channel_for_id (webrtc, new_id);
3688 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
3689 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream)
3691 const GstSDPMedia *local_media, *remote_media;
3692 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
3693 TransportReceiveBin *receive;
3694 int local_port, remote_port;
3695 guint64 local_max_size, remote_max_size, max_size;
3699 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
3702 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
3705 local_setup = _get_dtls_setup_from_media (local_media);
3706 remote_setup = _get_dtls_setup_from_media (remote_media);
3707 new_setup = _get_final_setup (local_setup, remote_setup);
3708 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
3711 /* data channel is always rtcp-muxed to avoid generating ICE candidates
3713 g_object_set (stream, "rtcp-mux", TRUE, "dtls-client",
3714 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
3716 local_port = _get_sctp_port_from_media (local_media);
3717 remote_port = _get_sctp_port_from_media (local_media);
3718 if (local_port == -1 || remote_port == -1)
3721 if (0 == (local_max_size =
3722 _get_sctp_max_message_size_from_media (local_media)))
3723 local_max_size = G_MAXUINT64;
3724 if (0 == (remote_max_size =
3725 _get_sctp_max_message_size_from_media (remote_media)))
3726 remote_max_size = G_MAXUINT64;
3727 max_size = MIN (local_max_size, remote_max_size);
3729 webrtc->priv->sctp_transport->max_message_size = max_size;
3732 guint orig_local_port, orig_remote_port;
3734 /* XXX: sctpassociation warns if we are in the wrong state */
3735 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
3736 &orig_local_port, NULL);
3738 if (orig_local_port != local_port)
3739 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
3742 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
3743 &orig_remote_port, NULL);
3744 if (orig_remote_port != remote_port)
3745 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
3749 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
3750 GstWebRTCDataChannel *channel;
3753 g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, i);
3755 if (channel->id == -1)
3756 channel->id = _generate_data_channel_id (webrtc);
3757 if (channel->id == -1)
3758 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
3759 ("%s", "Failed to generate an identifier for a data channel"), NULL);
3761 if (webrtc->priv->sctp_transport->association_established
3762 && !channel->negotiated && !channel->opened) {
3763 gst_webrtc_data_channel_link_to_sctp (channel,
3764 webrtc->priv->sctp_transport);
3765 gst_webrtc_data_channel_start_negotiation (channel);
3769 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
3770 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
3774 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
3779 if (p1->mline != -1)
3788 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
3791 GstPad *queue_srcpad;
3793 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
3798 if (webrtc->rtpfunnel)
3801 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
3802 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
3803 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
3805 queue = gst_element_factory_make ("queue", NULL);
3806 gst_bin_add (GST_BIN (webrtc), queue);
3807 gst_element_sync_state_with_parent (queue);
3809 gst_element_link (webrtc->rtpfunnel, queue);
3811 queue_srcpad = gst_element_get_static_pad (queue, "src");
3813 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
3814 rtp_sink = gst_element_get_request_pad (webrtc->rtpbin, pad_name);
3816 gst_pad_link (queue_srcpad, rtp_sink);
3817 gst_object_unref (queue_srcpad);
3818 gst_object_unref (rtp_sink);
3820 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
3821 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3822 GST_ELEMENT (stream->send_bin), "rtp_sink"))
3823 g_warn_if_reached ();
3831 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
3832 GstWebRTCSessionDescription * sdp)
3835 gboolean ret = FALSE;
3836 GStrv bundled = NULL;
3837 guint bundle_idx = 0;
3838 TransportStream *bundle_stream = NULL;
3840 if (!_parse_bundle (sdp->sdp, &bundled))
3845 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
3846 GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
3851 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
3852 _message_media_is_datachannel (sdp->sdp, bundle_idx));
3854 _connect_rtpfunnel (webrtc, bundle_idx);
3856 g_array_set_size (bundle_stream->ptmap, 0);
3857 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
3858 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
3862 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
3863 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
3864 TransportStream *stream;
3865 GstWebRTCRTPTransceiver *trans;
3866 guint transport_idx;
3868 /* skip rejected media */
3869 if (gst_sdp_media_get_port (media) == 0)
3873 transport_idx = bundle_idx;
3877 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
3879 stream = _get_or_create_transport_stream (webrtc, transport_idx,
3880 _message_media_is_datachannel (sdp->sdp, transport_idx));
3882 g_array_set_size (stream->ptmap, 0);
3883 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
3887 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
3889 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
3890 GST_ERROR ("State mismatch. Could not find local transceiver by mline.");
3893 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
3894 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
3896 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
3897 trans, bundled, bundle_idx);
3899 trans = _find_transceiver (webrtc, NULL,
3900 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
3901 /* XXX: default to the advertised direction in the sdp for new
3902 * transceviers. The spec doesn't actually say what happens here, only
3903 * that calls to setDirection will change the value. Nothing about
3904 * a default value when the transceiver is created internally */
3907 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
3908 _get_direction_from_media (media), i));
3910 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
3911 trans, bundled, bundle_idx);
3913 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
3914 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream);
3916 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
3924 g_strfreev (bundled);
3929 struct set_description
3931 GstPromise *promise;
3933 GstWebRTCSessionDescription *sdp;
3936 /* http://w3c.github.io/webrtc-pc/#set-description */
3938 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
3940 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
3941 gboolean signalling_state_changed = FALSE;
3942 GError *error = NULL;
3943 GStrv bundled = NULL;
3944 guint bundle_idx = 0;
3948 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
3949 webrtc->signaling_state);
3951 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
3952 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
3953 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
3954 _sdp_source_to_string (sd->source), type_str, state);
3955 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
3961 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error)) {
3962 GST_ERROR_OBJECT (webrtc, "%s", error->message);
3963 g_clear_error (&error);
3967 if (webrtc->priv->is_closed) {
3968 GST_WARNING_OBJECT (webrtc, "we are closed");
3972 if (!_parse_bundle (sd->sdp->sdp, &bundled))
3976 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
3977 GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
3983 switch (sd->sdp->type) {
3984 case GST_WEBRTC_SDP_TYPE_OFFER:{
3985 if (sd->source == SDP_LOCAL) {
3986 if (webrtc->pending_local_description)
3987 gst_webrtc_session_description_free
3988 (webrtc->pending_local_description);
3989 webrtc->pending_local_description =
3990 gst_webrtc_session_description_copy (sd->sdp);
3991 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
3993 if (webrtc->pending_remote_description)
3994 gst_webrtc_session_description_free
3995 (webrtc->pending_remote_description);
3996 webrtc->pending_remote_description =
3997 gst_webrtc_session_description_copy (sd->sdp);
3998 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
4002 case GST_WEBRTC_SDP_TYPE_ANSWER:{
4003 if (sd->source == SDP_LOCAL) {
4004 if (webrtc->current_local_description)
4005 gst_webrtc_session_description_free
4006 (webrtc->current_local_description);
4007 webrtc->current_local_description =
4008 gst_webrtc_session_description_copy (sd->sdp);
4010 if (webrtc->current_remote_description)
4011 gst_webrtc_session_description_free
4012 (webrtc->current_remote_description);
4013 webrtc->current_remote_description = webrtc->pending_remote_description;
4014 webrtc->pending_remote_description = NULL;
4016 if (webrtc->current_remote_description)
4017 gst_webrtc_session_description_free
4018 (webrtc->current_remote_description);
4019 webrtc->current_remote_description =
4020 gst_webrtc_session_description_copy (sd->sdp);
4022 if (webrtc->current_local_description)
4023 gst_webrtc_session_description_free
4024 (webrtc->current_local_description);
4025 webrtc->current_local_description = webrtc->pending_local_description;
4026 webrtc->pending_local_description = NULL;
4029 if (webrtc->pending_local_description)
4030 gst_webrtc_session_description_free (webrtc->pending_local_description);
4031 webrtc->pending_local_description = NULL;
4033 if (webrtc->pending_remote_description)
4034 gst_webrtc_session_description_free
4035 (webrtc->pending_remote_description);
4036 webrtc->pending_remote_description = NULL;
4038 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4041 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
4042 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
4043 if (sd->source == SDP_LOCAL) {
4044 if (webrtc->pending_local_description)
4045 gst_webrtc_session_description_free
4046 (webrtc->pending_local_description);
4047 webrtc->pending_local_description = NULL;
4049 if (webrtc->pending_remote_description)
4050 gst_webrtc_session_description_free
4051 (webrtc->pending_remote_description);
4052 webrtc->pending_remote_description = NULL;
4055 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4058 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
4059 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
4060 if (sd->source == SDP_LOCAL) {
4061 if (webrtc->pending_local_description)
4062 gst_webrtc_session_description_free
4063 (webrtc->pending_local_description);
4064 webrtc->pending_local_description =
4065 gst_webrtc_session_description_copy (sd->sdp);
4067 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
4069 if (webrtc->pending_remote_description)
4070 gst_webrtc_session_description_free
4071 (webrtc->pending_remote_description);
4072 webrtc->pending_remote_description =
4073 gst_webrtc_session_description_copy (sd->sdp);
4075 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
4081 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
4083 * If the mid value of an RTCRtpTransceiver was set to a non-null value
4084 * by the RTCSessionDescription that is being rolled back, set the mid
4085 * value of that transceiver to null, as described by [JSEP]
4086 * (section 4.1.7.2.).
4087 * If an RTCRtpTransceiver was created by applying the
4088 * RTCSessionDescription that is being rolled back, and a track has not
4089 * been attached to it via addTrack, remove that transceiver from
4090 * connection's set of transceivers, as described by [JSEP]
4091 * (section 4.1.7.2.).
4092 * Restore the value of connection's [[ sctpTransport]] internal slot
4093 * to its value at the last stable signaling state.
4097 if (webrtc->signaling_state != new_signaling_state) {
4098 webrtc->signaling_state = new_signaling_state;
4099 signalling_state_changed = TRUE;
4102 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
4105 /* media modifications */
4106 _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp);
4108 for (tmp = webrtc->priv->pending_sink_transceivers; tmp; tmp = tmp->next) {
4109 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
4110 const GstSDPMedia *media;
4112 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->mlineindex);
4113 /* skip rejected media */
4114 /* FIXME: arrange for an appropriate flow return */
4115 if (gst_sdp_media_get_port (media) == 0)
4118 _connect_input_stream (webrtc, pad);
4119 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
4123 g_list_free_full (webrtc->priv->pending_sink_transceivers,
4124 (GDestroyNotify) gst_object_unref);
4125 webrtc->priv->pending_sink_transceivers = NULL;
4128 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
4130 TransportStream *item;
4133 _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
4134 _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
4136 if (sd->source == SDP_REMOTE) {
4137 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
4140 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
4141 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
4143 if (g_strcmp0 (attr->key, "ssrc") == 0) {
4144 GStrv split = g_strsplit (attr->value, " ", 0);
4147 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
4148 && g_str_has_prefix (split[1], "cname:")) {
4149 SsrcMapItem ssrc_item;
4151 ssrc_item.media_idx = i;
4152 ssrc_item.ssrc = ssrc;
4153 g_array_append_val (item->remote_ssrcmap, ssrc_item);
4160 if (bundled && bundle_idx != i)
4163 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
4165 if (sd->source == SDP_LOCAL)
4166 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
4167 item->stream, ufrag, pwd);
4169 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
4170 item->stream, ufrag, pwd);
4175 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
4176 IceStreamItem *item =
4177 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
4179 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
4182 if (webrtc->current_local_description && webrtc->current_remote_description) {
4185 for (i = 0; i < webrtc->priv->pending_ice_candidates->len; i++) {
4186 IceCandidateItem *item =
4187 g_array_index (webrtc->priv->pending_ice_candidates,
4188 IceCandidateItem *, i);
4190 _add_ice_candidate (webrtc, item, TRUE);
4192 g_array_set_size (webrtc->priv->pending_ice_candidates, 0);
4196 * If connection's signaling state changed above, fire an event named
4197 * signalingstatechange at connection.
4199 if (signalling_state_changed) {
4200 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4201 webrtc->signaling_state);
4202 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4203 new_signaling_state);
4204 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
4207 g_object_notify (G_OBJECT (webrtc), "signaling-state");
4214 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
4215 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
4217 /* If connection's signaling state is now stable, update the
4218 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
4219 * was true both before and after this update, queue a task to check
4220 * connection's [[needNegotiation]] slot and, if still true, fire a
4221 * simple event named negotiationneeded at connection.*/
4222 _update_need_negotiation (webrtc);
4223 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
4224 _check_need_negotiation_task (webrtc, NULL);
4229 g_strfreev (bundled);
4232 gst_promise_reply (sd->promise, NULL);
4237 _free_set_description_data (struct set_description *sd)
4240 gst_promise_unref (sd->promise);
4242 gst_webrtc_session_description_free (sd->sdp);
4247 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
4248 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
4250 struct set_description *sd;
4252 if (remote_sdp == NULL)
4254 if (remote_sdp->sdp == NULL)
4257 sd = g_new0 (struct set_description, 1);
4258 if (promise != NULL)
4259 sd->promise = gst_promise_ref (promise);
4260 sd->source = SDP_REMOTE;
4261 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
4263 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
4264 sd, (GDestroyNotify) _free_set_description_data);
4270 gst_promise_reply (promise, NULL);
4271 g_return_if_reached ();
4276 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
4277 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
4279 struct set_description *sd;
4281 if (local_sdp == NULL)
4283 if (local_sdp->sdp == NULL)
4286 sd = g_new0 (struct set_description, 1);
4287 if (promise != NULL)
4288 sd->promise = gst_promise_ref (promise);
4289 sd->source = SDP_LOCAL;
4290 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
4292 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
4293 sd, (GDestroyNotify) _free_set_description_data);
4299 gst_promise_reply (promise, NULL);
4300 g_return_if_reached ();
4305 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
4307 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
4308 IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
4309 new->mlineindex = item->mlineindex;
4310 new->candidate = g_strdup (item->candidate);
4312 g_array_append_val (webrtc->priv->pending_ice_candidates, new);
4314 _add_ice_candidate (webrtc, item, FALSE);
4319 _free_ice_candidate_item (IceCandidateItem * item)
4321 _clear_ice_candidate_item (&item);
4325 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
4328 IceCandidateItem *item;
4330 item = g_new0 (IceCandidateItem, 1);
4331 item->mlineindex = mline;
4332 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
4333 item->candidate = g_strdup (attr);
4334 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
4335 item->candidate = g_strdup_printf ("a=%s", attr);
4336 gst_webrtc_bin_enqueue_task (webrtc,
4337 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
4338 (GDestroyNotify) _free_ice_candidate_item);
4342 _on_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
4344 const gchar *cand = item->candidate;
4346 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
4347 /* stripping away "a=" */
4351 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
4352 item->mlineindex, cand);
4355 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
4356 0, item->mlineindex, cand);
4361 _on_ice_candidate (GstWebRTCICE * ice, guint session_id,
4362 gchar * candidate, GstWebRTCBin * webrtc)
4364 IceCandidateItem *item = g_new0 (IceCandidateItem, 1);
4366 item->mlineindex = session_id;
4367 item->candidate = g_strdup (candidate);
4369 gst_webrtc_bin_enqueue_task (webrtc,
4370 (GstWebRTCBinFunc) _on_ice_candidate_task, item,
4371 (GDestroyNotify) _free_ice_candidate_item);
4374 /* https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm */
4375 static GstStructure *
4376 _get_stats_from_selector (GstWebRTCBin * webrtc, gpointer selector)
4379 GST_FIXME_OBJECT (webrtc, "Implement stats selection");
4381 return gst_structure_copy (webrtc->priv->stats);
4387 GstPromise *promise;
4391 _free_get_stats (struct get_stats *stats)
4394 gst_object_unref (stats->pad);
4396 gst_promise_unref (stats->promise);
4400 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
4402 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
4405 gpointer selector = NULL;
4407 gst_webrtc_bin_update_stats (webrtc);
4410 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (stats->pad);
4413 if (GST_PAD_DIRECTION (wpad) == GST_PAD_SRC) {
4414 selector = wpad->trans->receiver;
4416 selector = wpad->trans->sender;
4421 s = _get_stats_from_selector (webrtc, selector);
4422 gst_promise_reply (stats->promise, s);
4426 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
4427 GstPromise * promise)
4429 struct get_stats *stats;
4431 g_return_if_fail (promise != NULL);
4432 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
4434 stats = g_new0 (struct get_stats, 1);
4435 stats->promise = gst_promise_ref (promise);
4436 /* FIXME: check that pad exists in element */
4438 stats->pad = gst_object_ref (pad);
4440 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
4441 stats, (GDestroyNotify) _free_get_stats);
4444 static GstWebRTCRTPTransceiver *
4445 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
4446 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
4448 WebRTCTransceiver *trans;
4449 GstWebRTCRTPTransceiver *rtp_trans;
4451 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
4454 trans = _create_webrtc_transceiver (webrtc, direction, -1);
4455 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4457 rtp_trans->codec_preferences = gst_caps_ref (caps);
4459 return gst_object_ref (trans);
4463 _deref_and_unref (GstObject ** object)
4466 gst_object_unref (*object);
4470 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
4472 GArray *arr = g_array_new (FALSE, TRUE, sizeof (gpointer));
4475 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
4477 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4478 GstWebRTCRTPTransceiver *trans =
4479 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
4481 gst_object_ref (trans);
4482 g_array_append_val (arr, trans);
4488 static GstWebRTCRTPTransceiver *
4489 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
4491 GstWebRTCRTPTransceiver *trans = NULL;
4493 if (idx >= webrtc->priv->transceivers->len) {
4494 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
4499 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
4501 gst_object_ref (trans);
4508 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
4510 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
4511 g_return_val_if_fail (uri != NULL, FALSE);
4513 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
4515 return gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
4519 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
4521 GstPad *gpad = GST_PAD_CAST (user_data);
4523 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
4524 gst_pad_store_sticky_event (gpad, *event);
4529 static GstWebRTCDataChannel *
4530 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
4531 GstStructure * init_params)
4534 gint max_packet_lifetime;
4535 gint max_retransmits;
4536 const gchar *protocol;
4537 gboolean negotiated;
4539 GstWebRTCPriorityType priority;
4540 GstWebRTCDataChannel *ret;
4541 gint max_channels = 65534;
4543 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
4544 g_return_val_if_fail (label != NULL, NULL);
4545 g_return_val_if_fail (strlen (label) <= 65535, NULL);
4546 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
4549 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
4552 || !gst_structure_get_int (init_params, "max-packet-lifetime",
4553 &max_packet_lifetime))
4554 max_packet_lifetime = -1;
4556 || !gst_structure_get_int (init_params, "max-retransmits",
4558 max_retransmits = -1;
4559 /* both retransmits and lifetime cannot be set */
4560 g_return_val_if_fail ((max_packet_lifetime == -1)
4561 || (max_retransmits == -1), NULL);
4564 || !(protocol = gst_structure_get_string (init_params, "protocol")))
4566 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
4569 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
4571 if (!negotiated || !init_params
4572 || !gst_structure_get_int (init_params, "id", &id))
4575 g_return_val_if_fail (id != -1, NULL);
4576 g_return_val_if_fail (id < 65535, NULL);
4579 || !gst_structure_get_enum (init_params, "priority",
4580 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
4581 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
4583 /* FIXME: clamp max-retransmits and max-packet-lifetime */
4585 if (webrtc->priv->sctp_transport) {
4586 /* Let transport be the connection's [[SctpTransport]] slot.
4588 * If the [[DataChannelId]] slot is not null, transport is in
4589 * connected state and [[DataChannelId]] is greater or equal to the
4590 * transport's [[MaxChannels]] slot, throw an OperationError.
4592 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
4595 g_return_val_if_fail (id <= max_channels, NULL);
4598 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
4599 !_have_sctp_elements (webrtc))
4603 /* check if the id has been used already */
4605 GstWebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
4607 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
4608 ("Attempting to add a data channel with a duplicate ID: %i", id),
4613 } else if (webrtc->current_local_description
4614 && webrtc->current_remote_description && webrtc->priv->sctp_transport
4615 && webrtc->priv->sctp_transport->transport) {
4616 /* else we can only generate an id if we're configured already. The other
4617 * case for generating an id is on sdp setting */
4618 id = _generate_data_channel_id (webrtc);
4620 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
4621 ("%s", "Failed to generate an identifier for a data channel"), NULL);
4627 ret = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, "label", label,
4628 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
4629 "max-retransmits", max_retransmits, "protocol", protocol,
4630 "negotiated", negotiated, "id", id, "priority", priority, NULL);
4633 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
4634 gst_bin_add (GST_BIN (webrtc), ret->appsink);
4636 gst_element_sync_state_with_parent (ret->appsrc);
4637 gst_element_sync_state_with_parent (ret->appsink);
4639 ret = gst_object_ref (ret);
4640 ret->webrtcbin = webrtc;
4641 g_array_append_val (webrtc->priv->data_channels, ret);
4642 gst_webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
4643 if (webrtc->priv->sctp_transport &&
4644 webrtc->priv->sctp_transport->association_established
4645 && !ret->negotiated) {
4646 gst_webrtc_data_channel_start_negotiation (ret);
4648 _update_need_negotiation (webrtc);
4656 /* === rtpbin signal implementations === */
4659 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
4660 GstWebRTCBin * webrtc)
4662 gchar *new_pad_name = NULL;
4664 new_pad_name = gst_pad_get_name (new_pad);
4665 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
4666 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
4667 guint32 session_id = 0, ssrc = 0, pt = 0;
4668 GstWebRTCRTPTransceiver *rtp_trans;
4669 WebRTCTransceiver *trans;
4670 TransportStream *stream;
4671 GstWebRTCBinPad *pad;
4672 guint media_idx = 0;
4673 gboolean found_ssrc = FALSE;
4676 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
4678 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
4682 stream = _find_transport_for_session (webrtc, session_id);
4684 g_warn_if_reached ();
4686 media_idx = session_id;
4688 for (i = 0; i < stream->remote_ssrcmap->len; i++) {
4690 &g_array_index (stream->remote_ssrcmap, SsrcMapItem, i);
4691 if (item->ssrc == ssrc) {
4692 media_idx = item->media_idx;
4699 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
4702 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
4704 g_warn_if_reached ();
4705 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4706 g_assert (trans->stream == stream);
4708 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4710 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
4711 " for rtpbin pad name %s", pad, new_pad_name);
4713 g_warn_if_reached ();
4714 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
4716 if (webrtc->priv->running)
4717 gst_pad_set_active (GST_PAD (pad), TRUE);
4718 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
4719 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
4720 _remove_pending_pad (webrtc, pad);
4722 gst_object_unref (pad);
4724 g_free (new_pad_name);
4727 /* only used for the receiving streams */
4729 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
4730 GstWebRTCBin * webrtc)
4732 TransportStream *stream;
4735 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
4738 stream = _find_transport_for_session (webrtc, session_id);
4740 goto unknown_session;
4742 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
4745 GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
4746 "session %d", ret, pt, session_id);
4752 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
4758 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
4759 GstWebRTCBin * webrtc)
4761 TransportStream *stream;
4762 gboolean have_rtx = FALSE;
4763 GstStructure *pt_map = NULL;
4764 GstElement *ret = NULL;
4765 GstWebRTCRTPTransceiver *trans;
4767 stream = _find_transport_for_session (webrtc, session_id);
4768 trans = _find_transceiver (webrtc, &session_id,
4769 (FindTransceiverFunc) transceiver_match_for_mline);
4772 have_rtx = transport_stream_get_pt (stream, "RTX") != 0;
4774 GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
4775 " with transport %" GST_PTR_FORMAT " and pt map %" GST_PTR_FORMAT, stream,
4783 if (stream->rtxsend) {
4784 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
4788 GST_INFO ("creating AUX sender");
4789 ret = gst_bin_new (NULL);
4790 rtx = gst_element_factory_make ("rtprtxsend", NULL);
4791 g_object_set (rtx, "max-size-packets", 500, NULL);
4792 _set_rtx_ptmap_from_stream (webrtc, stream);
4794 if (WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map)
4795 g_object_set (rtx, "ssrc-map",
4796 WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map, NULL);
4798 gst_bin_add (GST_BIN (ret), rtx);
4800 pad = gst_element_get_static_pad (rtx, "src");
4801 name = g_strdup_printf ("src_%u", session_id);
4802 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
4804 gst_object_unref (pad);
4806 pad = gst_element_get_static_pad (rtx, "sink");
4807 name = g_strdup_printf ("sink_%u", session_id);
4808 gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
4810 gst_object_unref (pad);
4812 stream->rtxsend = gst_object_ref (rtx);
4817 gst_structure_free (pt_map);
4823 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
4824 GstWebRTCBin * webrtc)
4826 GstElement *ret = NULL;
4827 GstElement *prev = NULL;
4828 GstPad *sinkpad = NULL;
4829 TransportStream *stream;
4833 stream = _find_transport_for_session (webrtc, session_id);
4836 red_pt = transport_stream_get_pt (stream, "RED");
4837 rtx_pt = transport_stream_get_pt (stream, "RTX");
4840 GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
4843 if (red_pt || rtx_pt)
4844 ret = gst_bin_new (NULL);
4847 if (stream->rtxreceive) {
4848 GST_WARNING_OBJECT (webrtc,
4849 "rtprtxreceive already created! rtpbin bug?!");
4853 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
4854 _set_rtx_ptmap_from_stream (webrtc, stream);
4856 gst_bin_add (GST_BIN (ret), stream->rtxreceive);
4858 sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
4860 prev = gst_object_ref (stream->rtxreceive);
4864 GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
4866 GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
4867 red_pt, session_id);
4869 gst_bin_add (GST_BIN (ret), rtpreddec);
4871 g_object_set (rtpreddec, "pt", red_pt, NULL);
4874 gst_element_link (prev, rtpreddec);
4876 sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
4882 gchar *name = g_strdup_printf ("sink_%u", session_id);
4883 GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
4885 gst_object_unref (sinkpad);
4886 gst_element_add_pad (ret, ghost);
4890 gchar *name = g_strdup_printf ("src_%u", session_id);
4891 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
4892 GstPad *ghost = gst_ghost_pad_new (name, srcpad);
4894 gst_object_unref (srcpad);
4895 gst_element_add_pad (ret, ghost);
4903 gst_object_unref (ret);
4908 on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
4909 GstWebRTCBin * webrtc)
4911 TransportStream *stream;
4912 GstElement *ret = NULL;
4914 GObject *internal_storage;
4916 stream = _find_transport_for_session (webrtc, session_id);
4918 /* TODO: for now, we only support ulpfec, but once we support
4919 * more algorithms, if the remote may use more than one algorithm,
4920 * we will want to do the following:
4922 * + Return a bin here, with the relevant FEC decoders plugged in
4923 * and their payload type set to 0
4924 * + Enable the decoders by setting the payload type only when
4925 * we detect it (by connecting to ptdemux:new-payload-type for
4929 pt = transport_stream_get_pt (stream, "ULPFEC");
4932 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
4934 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
4935 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
4938 g_object_set (ret, "pt", pt, "storage", internal_storage, NULL);
4939 g_object_unref (internal_storage);
4946 on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
4947 GstWebRTCBin * webrtc)
4949 GstElement *ret = NULL;
4950 GstElement *prev = NULL;
4951 TransportStream *stream;
4952 guint ulpfec_pt = 0;
4954 GstPad *sinkpad = NULL;
4955 GstWebRTCRTPTransceiver *trans;
4957 stream = _find_transport_for_session (webrtc, session_id);
4958 trans = _find_transceiver (webrtc, &session_id,
4959 (FindTransceiverFunc) transceiver_match_for_mline);
4962 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
4963 red_pt = transport_stream_get_pt (stream, "RED");
4966 if (ulpfec_pt || red_pt)
4967 ret = gst_bin_new (NULL);
4970 GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4971 GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
4973 GST_DEBUG_OBJECT (webrtc,
4974 "Creating ULPFEC encoder for session %d with pt %d", session_id,
4977 gst_bin_add (GST_BIN (ret), fecenc);
4978 sinkpad = gst_element_get_static_pad (fecenc, "sink");
4979 g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
4980 WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
4983 if (caps && !gst_caps_is_empty (caps)) {
4984 const GstStructure *s = gst_caps_get_structure (caps, 0);
4985 const gchar *media = gst_structure_get_string (s, "media");
4987 if (!g_strcmp0 (media, "video"))
4988 g_object_set (fecenc, "multipacket", TRUE, NULL);
4995 GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
4997 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
4998 session_id, red_pt);
5000 gst_bin_add (GST_BIN (ret), redenc);
5002 gst_element_link (prev, redenc);
5004 sinkpad = gst_element_get_static_pad (redenc, "sink");
5006 g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
5012 GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
5013 gst_object_unref (sinkpad);
5014 gst_element_add_pad (ret, ghost);
5018 GstPad *srcpad = gst_element_get_static_pad (prev, "src");
5019 GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
5020 gst_object_unref (srcpad);
5021 gst_element_add_pad (ret, ghost);
5028 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
5029 GstWebRTCBin * webrtc)
5031 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
5035 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
5036 GstWebRTCBin * webrtc)
5038 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
5042 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
5043 GstWebRTCBin * webrtc)
5045 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
5050 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
5051 GstWebRTCBin * webrtc)
5053 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
5057 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
5058 GstWebRTCBin * webrtc)
5060 GST_INFO_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
5064 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
5065 GstWebRTCBin * webrtc)
5067 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
5071 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
5072 GstWebRTCBin * webrtc)
5074 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
5078 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
5079 GstWebRTCBin * webrtc)
5081 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
5085 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
5086 GstWebRTCBin * webrtc)
5088 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
5092 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
5093 GstWebRTCBin * webrtc)
5095 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
5100 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
5101 GstWebRTCBin * webrtc)
5103 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
5108 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
5109 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
5111 GstWebRTCRTPTransceiver *trans;
5113 trans = _find_transceiver (webrtc, &session_id,
5114 (FindTransceiverFunc) transceiver_match_for_mline);
5117 /* We don't set do-retransmission on rtpbin as we want per-session control */
5118 g_object_set (jitterbuffer, "do-retransmission",
5119 WEBRTC_TRANSCEIVER (trans)->do_nack, NULL);
5121 g_assert_not_reached ();
5126 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
5127 guint session_id, GstWebRTCBin * webrtc)
5129 /* TODO: when exposing latency, set size-time based on that */
5130 g_object_set (storage, "size-time", (guint64) 250 * GST_MSECOND, NULL);
5134 _create_rtpbin (GstWebRTCBin * webrtc)
5138 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
5141 /* mandated by WebRTC */
5142 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
5144 g_object_set (rtpbin, "do-lost", TRUE, NULL);
5146 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
5148 g_signal_connect (rtpbin, "request-pt-map",
5149 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
5150 g_signal_connect (rtpbin, "request-aux-sender",
5151 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
5152 g_signal_connect (rtpbin, "request-aux-receiver",
5153 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
5154 g_signal_connect (rtpbin, "new-storage",
5155 G_CALLBACK (on_rtpbin_new_storage), webrtc);
5156 g_signal_connect (rtpbin, "request-fec-decoder",
5157 G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
5158 g_signal_connect (rtpbin, "request-fec-encoder",
5159 G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
5160 g_signal_connect (rtpbin, "on-bye-ssrc",
5161 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
5162 g_signal_connect (rtpbin, "on-bye-timeout",
5163 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
5164 g_signal_connect (rtpbin, "on-new-ssrc",
5165 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
5166 g_signal_connect (rtpbin, "on-new-sender-ssrc",
5167 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
5168 g_signal_connect (rtpbin, "on-sender-ssrc-active",
5169 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
5170 g_signal_connect (rtpbin, "on-sender-timeout",
5171 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
5172 g_signal_connect (rtpbin, "on-ssrc-active",
5173 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
5174 g_signal_connect (rtpbin, "on-ssrc-collision",
5175 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
5176 g_signal_connect (rtpbin, "on-ssrc-sdes",
5177 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
5178 g_signal_connect (rtpbin, "on-ssrc-validated",
5179 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
5180 g_signal_connect (rtpbin, "on-timeout",
5181 G_CALLBACK (on_rtpbin_timeout), webrtc);
5182 g_signal_connect (rtpbin, "new-jitterbuffer",
5183 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
5188 static GstStateChangeReturn
5189 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
5191 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
5192 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
5194 GST_DEBUG ("changing state: %s => %s",
5195 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
5196 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
5198 switch (transition) {
5199 case GST_STATE_CHANGE_NULL_TO_READY:{
5200 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
5201 return GST_STATE_CHANGE_FAILURE;
5202 _start_thread (webrtc);
5204 _update_need_negotiation (webrtc);
5208 case GST_STATE_CHANGE_READY_TO_PAUSED:
5209 webrtc->priv->running = TRUE;
5215 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5216 if (ret == GST_STATE_CHANGE_FAILURE)
5219 switch (transition) {
5220 case GST_STATE_CHANGE_READY_TO_PAUSED:
5221 /* Mangle the return value to NO_PREROLL as that's what really is
5222 * occurring here however cannot be propagated correctly due to nicesrc
5223 * requiring that it be in PLAYING already in order to send/receive
5225 ret = GST_STATE_CHANGE_NO_PREROLL;
5227 case GST_STATE_CHANGE_PAUSED_TO_READY:
5228 webrtc->priv->running = FALSE;
5230 case GST_STATE_CHANGE_READY_TO_NULL:
5231 _stop_thread (webrtc);
5240 static GstPadProbeReturn
5241 pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
5243 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
5245 return GST_PAD_PROBE_OK;
5249 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
5250 const gchar * name, const GstCaps * caps)
5252 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
5253 GstWebRTCBinPad *pad = NULL;
5256 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
5259 if (templ->direction == GST_PAD_SINK ||
5260 g_strcmp0 (templ->name_template, "sink_%u") == 0) {
5261 GstWebRTCRTPTransceiver *trans;
5263 GST_OBJECT_LOCK (webrtc);
5264 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
5265 /* no name given when requesting the pad, use next available int */
5266 serial = webrtc->priv->max_sink_pad_serial++;
5268 /* parse serial number from requested padname */
5269 serial = g_ascii_strtoull (&name[5], NULL, 10);
5270 if (serial > webrtc->priv->max_sink_pad_serial)
5271 webrtc->priv->max_sink_pad_serial = serial;
5273 GST_OBJECT_UNLOCK (webrtc);
5275 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial);
5276 trans = _find_transceiver_for_mline (webrtc, serial);
5279 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
5280 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, serial));
5281 pad->trans = gst_object_ref (trans);
5283 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
5284 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
5285 (GstPadProbeCallback) pad_block, NULL, NULL);
5286 webrtc->priv->pending_sink_transceivers =
5287 g_list_append (webrtc->priv->pending_sink_transceivers,
5288 gst_object_ref (pad));
5289 _add_pad (webrtc, pad);
5292 return GST_PAD (pad);
5296 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
5298 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
5299 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
5301 if (webrtc_pad->trans)
5302 gst_object_unref (webrtc_pad->trans);
5303 webrtc_pad->trans = NULL;
5305 _remove_pad (webrtc, webrtc_pad);
5307 _update_need_negotiation (webrtc);
5312 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
5313 const GValue * value, GParamSpec * pspec)
5315 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5318 case PROP_STUN_SERVER:
5319 case PROP_TURN_SERVER:
5320 g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
5322 case PROP_BUNDLE_POLICY:
5323 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
5324 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
5326 webrtc->bundle_policy = g_value_get_enum (value);
5329 case PROP_ICE_TRANSPORT_POLICY:
5330 webrtc->ice_transport_policy = g_value_get_enum (value);
5331 g_object_set (webrtc->priv->ice, "force-relay",
5332 webrtc->ice_transport_policy ==
5333 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE, NULL);
5336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5342 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
5343 GValue * value, GParamSpec * pspec)
5345 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5349 case PROP_CONNECTION_STATE:
5350 g_value_set_enum (value, webrtc->peer_connection_state);
5352 case PROP_SIGNALING_STATE:
5353 g_value_set_enum (value, webrtc->signaling_state);
5355 case PROP_ICE_GATHERING_STATE:
5356 g_value_set_enum (value, webrtc->ice_gathering_state);
5358 case PROP_ICE_CONNECTION_STATE:
5359 g_value_set_enum (value, webrtc->ice_connection_state);
5361 case PROP_LOCAL_DESCRIPTION:
5362 if (webrtc->pending_local_description)
5363 g_value_set_boxed (value, webrtc->pending_local_description);
5364 else if (webrtc->current_local_description)
5365 g_value_set_boxed (value, webrtc->current_local_description);
5367 g_value_set_boxed (value, NULL);
5369 case PROP_CURRENT_LOCAL_DESCRIPTION:
5370 g_value_set_boxed (value, webrtc->current_local_description);
5372 case PROP_PENDING_LOCAL_DESCRIPTION:
5373 g_value_set_boxed (value, webrtc->pending_local_description);
5375 case PROP_REMOTE_DESCRIPTION:
5376 if (webrtc->pending_remote_description)
5377 g_value_set_boxed (value, webrtc->pending_remote_description);
5378 else if (webrtc->current_remote_description)
5379 g_value_set_boxed (value, webrtc->current_remote_description);
5381 g_value_set_boxed (value, NULL);
5383 case PROP_CURRENT_REMOTE_DESCRIPTION:
5384 g_value_set_boxed (value, webrtc->current_remote_description);
5386 case PROP_PENDING_REMOTE_DESCRIPTION:
5387 g_value_set_boxed (value, webrtc->pending_remote_description);
5389 case PROP_STUN_SERVER:
5390 case PROP_TURN_SERVER:
5391 g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
5393 case PROP_BUNDLE_POLICY:
5394 g_value_set_enum (value, webrtc->bundle_policy);
5396 case PROP_ICE_TRANSPORT_POLICY:
5397 g_value_set_enum (value, webrtc->ice_transport_policy);
5400 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5407 _free_pending_pad (GstPad * pad)
5409 gst_object_unref (pad);
5413 gst_webrtc_bin_dispose (GObject * object)
5415 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5417 if (webrtc->priv->ice)
5418 gst_object_unref (webrtc->priv->ice);
5419 webrtc->priv->ice = NULL;
5421 if (webrtc->priv->ice_stream_map)
5422 g_array_free (webrtc->priv->ice_stream_map, TRUE);
5423 webrtc->priv->ice_stream_map = NULL;
5425 g_clear_object (&webrtc->priv->sctp_transport);
5427 G_OBJECT_CLASS (parent_class)->dispose (object);
5431 gst_webrtc_bin_finalize (GObject * object)
5433 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5435 if (webrtc->priv->transports)
5436 g_array_free (webrtc->priv->transports, TRUE);
5437 webrtc->priv->transports = NULL;
5439 if (webrtc->priv->transceivers)
5440 g_array_free (webrtc->priv->transceivers, TRUE);
5441 webrtc->priv->transceivers = NULL;
5443 if (webrtc->priv->data_channels)
5444 g_array_free (webrtc->priv->data_channels, TRUE);
5445 webrtc->priv->data_channels = NULL;
5447 if (webrtc->priv->pending_data_channels)
5448 g_array_free (webrtc->priv->pending_data_channels, TRUE);
5449 webrtc->priv->pending_data_channels = NULL;
5451 if (webrtc->priv->pending_ice_candidates)
5452 g_array_free (webrtc->priv->pending_ice_candidates, TRUE);
5453 webrtc->priv->pending_ice_candidates = NULL;
5455 if (webrtc->priv->session_mid_map)
5456 g_array_free (webrtc->priv->session_mid_map, TRUE);
5457 webrtc->priv->session_mid_map = NULL;
5459 if (webrtc->priv->pending_pads)
5460 g_list_free_full (webrtc->priv->pending_pads,
5461 (GDestroyNotify) _free_pending_pad);
5462 webrtc->priv->pending_pads = NULL;
5464 if (webrtc->priv->pending_sink_transceivers)
5465 g_list_free_full (webrtc->priv->pending_sink_transceivers,
5466 (GDestroyNotify) gst_object_unref);
5467 webrtc->priv->pending_sink_transceivers = NULL;
5469 if (webrtc->current_local_description)
5470 gst_webrtc_session_description_free (webrtc->current_local_description);
5471 webrtc->current_local_description = NULL;
5472 if (webrtc->pending_local_description)
5473 gst_webrtc_session_description_free (webrtc->pending_local_description);
5474 webrtc->pending_local_description = NULL;
5476 if (webrtc->current_remote_description)
5477 gst_webrtc_session_description_free (webrtc->current_remote_description);
5478 webrtc->current_remote_description = NULL;
5479 if (webrtc->pending_remote_description)
5480 gst_webrtc_session_description_free (webrtc->pending_remote_description);
5481 webrtc->pending_remote_description = NULL;
5483 if (webrtc->priv->last_generated_answer)
5484 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
5485 webrtc->priv->last_generated_answer = NULL;
5486 if (webrtc->priv->last_generated_offer)
5487 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
5488 webrtc->priv->last_generated_offer = NULL;
5490 if (webrtc->priv->stats)
5491 gst_structure_free (webrtc->priv->stats);
5492 webrtc->priv->stats = NULL;
5494 g_mutex_clear (PC_GET_LOCK (webrtc));
5495 g_cond_clear (PC_GET_COND (webrtc));
5497 G_OBJECT_CLASS (parent_class)->finalize (object);
5501 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
5503 GObjectClass *gobject_class = (GObjectClass *) klass;
5504 GstElementClass *element_class = (GstElementClass *) klass;
5506 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
5507 element_class->release_pad = gst_webrtc_bin_release_pad;
5508 element_class->change_state = gst_webrtc_bin_change_state;
5510 gst_element_class_add_static_pad_template_with_gtype (element_class,
5511 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
5512 gst_element_class_add_static_pad_template (element_class, &src_template);
5514 gst_element_class_set_metadata (element_class, "WebRTC Bin",
5515 "Filter/Network/WebRTC", "A bin for webrtc connections",
5516 "Matthew Waters <matthew@centricular.com>");
5518 gobject_class->get_property = gst_webrtc_bin_get_property;
5519 gobject_class->set_property = gst_webrtc_bin_set_property;
5520 gobject_class->dispose = gst_webrtc_bin_dispose;
5521 gobject_class->finalize = gst_webrtc_bin_finalize;
5523 g_object_class_install_property (gobject_class,
5524 PROP_LOCAL_DESCRIPTION,
5525 g_param_spec_boxed ("local-description", "Local Description",
5526 "The local SDP description in use for this connection. "
5527 "Favours a pending description over the current description",
5528 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5529 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5531 g_object_class_install_property (gobject_class,
5532 PROP_CURRENT_LOCAL_DESCRIPTION,
5533 g_param_spec_boxed ("current-local-description",
5534 "Current Local Description",
5535 "The local description that was successfully negotiated the last time "
5536 "the connection transitioned into the stable state",
5537 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5538 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5540 g_object_class_install_property (gobject_class,
5541 PROP_PENDING_LOCAL_DESCRIPTION,
5542 g_param_spec_boxed ("pending-local-description",
5543 "Pending Local Description",
5544 "The local description that is in the process of being negotiated plus "
5545 "any local candidates that have been generated by the ICE Agent since the "
5546 "offer or answer was created",
5547 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5548 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5550 g_object_class_install_property (gobject_class,
5551 PROP_REMOTE_DESCRIPTION,
5552 g_param_spec_boxed ("remote-description", "Remote Description",
5553 "The remote SDP description to use for this connection. "
5554 "Favours a pending description over the current description",
5555 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5556 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5558 g_object_class_install_property (gobject_class,
5559 PROP_CURRENT_REMOTE_DESCRIPTION,
5560 g_param_spec_boxed ("current-remote-description",
5561 "Current Remote Description",
5562 "The last remote description that was successfully negotiated the last "
5563 "time the connection transitioned into the stable state plus any remote "
5564 "candidates that have been supplied via addIceCandidate() since the offer "
5565 "or answer was created",
5566 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5567 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5569 g_object_class_install_property (gobject_class,
5570 PROP_PENDING_REMOTE_DESCRIPTION,
5571 g_param_spec_boxed ("pending-remote-description",
5572 "Pending Remote Description",
5573 "The remote description that is in the process of being negotiated, "
5574 "complete with any remote candidates that have been supplied via "
5575 "addIceCandidate() since the offer or answer was created",
5576 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5577 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5579 g_object_class_install_property (gobject_class,
5581 g_param_spec_string ("stun-server", "STUN Server",
5582 "The STUN server of the form stun://hostname:port",
5583 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5585 g_object_class_install_property (gobject_class,
5587 g_param_spec_string ("turn-server", "TURN Server",
5588 "The TURN server of the form turn(s)://username:password@host:port. "
5589 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
5590 "if you wish to use multiple TURN servers",
5591 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5593 g_object_class_install_property (gobject_class,
5594 PROP_CONNECTION_STATE,
5595 g_param_spec_enum ("connection-state", "Connection State",
5596 "The overall connection state of this element",
5597 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
5598 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
5599 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5601 g_object_class_install_property (gobject_class,
5602 PROP_SIGNALING_STATE,
5603 g_param_spec_enum ("signaling-state", "Signaling State",
5604 "The signaling state of this element",
5605 GST_TYPE_WEBRTC_SIGNALING_STATE,
5606 GST_WEBRTC_SIGNALING_STATE_STABLE,
5607 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5609 g_object_class_install_property (gobject_class,
5610 PROP_ICE_CONNECTION_STATE,
5611 g_param_spec_enum ("ice-connection-state", "ICE connection state",
5612 "The collective connection state of all ICETransport's",
5613 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
5614 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
5615 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5617 g_object_class_install_property (gobject_class,
5618 PROP_ICE_GATHERING_STATE,
5619 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
5620 "The collective gathering state of all ICETransport's",
5621 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
5622 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
5623 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5625 g_object_class_install_property (gobject_class,
5627 g_param_spec_enum ("bundle-policy", "Bundle Policy",
5628 "The policy to apply for bundling",
5629 GST_TYPE_WEBRTC_BUNDLE_POLICY,
5630 GST_WEBRTC_BUNDLE_POLICY_NONE,
5631 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5633 g_object_class_install_property (gobject_class,
5634 PROP_ICE_TRANSPORT_POLICY,
5635 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
5636 "The policy to apply for ICE transport",
5637 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
5638 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
5639 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5642 * GstWebRTCBin::create-offer:
5643 * @object: the #webrtcbin
5644 * @options: (nullable): create-offer options
5645 * @promise: a #GstPromise which will contain the offer
5647 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
5648 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
5649 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5650 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL,
5651 g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
5655 * GstWebRTCBin::create-answer:
5656 * @object: the #webrtcbin
5657 * @options: (nullable): create-answer options
5658 * @promise: a #GstPromise which will contain the answer
5660 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
5661 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
5662 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5663 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL,
5664 g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
5668 * GstWebRTCBin::set-local-description:
5669 * @object: the #GstWebRTCBin
5670 * @desc: a #GstWebRTCSessionDescription description
5671 * @promise: (nullable): a #GstPromise to be notified when it's set
5673 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
5674 g_signal_new_class_handler ("set-local-description",
5675 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5676 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL,
5677 g_cclosure_marshal_generic, G_TYPE_NONE, 2,
5678 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
5681 * GstWebRTCBin::set-remote-description:
5682 * @object: the #GstWebRTCBin
5683 * @desc: a #GstWebRTCSessionDescription description
5684 * @promise: (nullable): a #GstPromise to be notified when it's set
5686 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
5687 g_signal_new_class_handler ("set-remote-description",
5688 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5689 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL,
5690 g_cclosure_marshal_generic, G_TYPE_NONE, 2,
5691 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
5694 * GstWebRTCBin::add-ice-candidate:
5695 * @object: the #webrtcbin
5696 * @mline_index: the index of the media description in the SDP
5697 * @ice-candidate: an ice candidate
5699 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
5700 g_signal_new_class_handler ("add-ice-candidate",
5701 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5702 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL,
5703 g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
5706 * GstWebRTCBin::get-stats:
5707 * @object: the #webrtcbin
5708 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
5709 * @promise: a #GstPromise for the result
5711 * The @promise will contain the result of retrieving the session statistics.
5712 * The structure will be named 'application/x-webrtc-stats and contain the
5713 * following based on the webrtc-stats spec available from
5714 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
5715 * and is constantly changing these statistics may be changed to fit with
5718 * Each field key is a unique identifier for each RTCStats
5719 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
5720 * GstStructure) in the RTCStatsReport
5721 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
5722 * field in the RTCStats subclass is outlined below.
5724 * Each statistics structure contains the following values as defined by
5725 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
5727 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
5728 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
5729 * "id" G_TYPE_STRING unique identifier
5731 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
5733 * "payload-type" G_TYPE_UINT the rtp payload number in use
5734 * "clock-rate" G_TYPE_UINT the rtp clock-rate
5736 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
5738 * "ssrc" G_TYPE_STRING the rtp sequence src in use
5739 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
5740 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
5741 * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
5742 * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
5743 * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
5745 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
5747 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
5748 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
5749 * "packets-lost" G_TYPE_UINT number of packets lost
5750 * "jitter" G_TYPE_DOUBLE packet jitter measured in secondss
5752 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
5754 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
5756 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
5758 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
5759 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
5761 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
5763 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
5764 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
5766 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
5768 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
5770 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
5772 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
5775 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
5776 g_signal_new_class_handler ("get-stats",
5777 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5778 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL,
5779 g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_PAD,
5783 * GstWebRTCBin::on-negotiation-needed:
5784 * @object: the #webrtcbin
5786 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
5787 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
5788 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5792 * GstWebRTCBin::on-ice-candidate:
5793 * @object: the #webrtcbin
5794 * @mline_index: the index of the media description in the SDP
5795 * @candidate: the ICE candidate
5797 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
5798 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
5799 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5800 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
5803 * GstWebRTCBin::on-new-transceiver:
5804 * @object: the #webrtcbin
5805 * @candidate: the new #GstWebRTCRTPTransceiver
5807 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
5808 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
5809 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5810 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
5813 * GstWebRTCBin::on-data-channel:
5814 * @object: the #GstWebRTCBin
5815 * @candidate: the new `GstWebRTCDataChannel`
5817 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
5818 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
5819 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5820 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
5823 * GstWebRTCBin::add-transceiver:
5824 * @object: the #webrtcbin
5825 * @direction: the direction of the new transceiver
5826 * @caps: (allow none): the codec preferences for this transceiver
5828 * Returns: the new #GstWebRTCRTPTransceiver
5830 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
5831 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
5832 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5833 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
5834 g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
5835 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
5838 * GstWebRTCBin::get-transceivers:
5839 * @object: the #webrtcbin
5841 * Returns: a #GArray of #GstWebRTCRTPTransceivers
5843 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
5844 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
5845 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5846 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL,
5847 g_cclosure_marshal_generic, G_TYPE_ARRAY, 0);
5850 * GstWebRTCBin::get-transceiver:
5851 * @object: the #GstWebRTCBin
5852 * @idx: The index of the transceiver
5854 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
5857 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
5858 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
5859 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5860 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL,
5861 g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1,
5865 * GstWebRTCBin::add-turn-server:
5866 * @object: the #GstWebRTCBin
5867 * @uri: The uri of the server of the form turn(s)://username:password@host:port
5869 * Add a turn server to obtain ICE candidates from
5871 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
5872 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
5873 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5874 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL,
5875 g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
5878 * GstWebRTCBin::create-data-channel:
5879 * @object: the #GstWebRTCBin
5880 * @label: the label for the data channel
5881 * @options: a #GstStructure of options for creating the data channel
5883 * The options dictionary is the same format as the RTCDataChannelInit
5884 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
5885 * and reproduced below
5887 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
5888 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
5889 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
5890 * protocol G_TYPE_STRING The subprotocol used by this channel
5891 * 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.
5892 * id G_TYPE_INT Override the default identifier selection of this channel
5893 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
5895 * Returns: (transfer full): a new data channel object
5897 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
5898 g_signal_new_class_handler ("create-data-channel",
5899 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5900 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
5901 g_cclosure_marshal_generic, GST_TYPE_WEBRTC_DATA_CHANNEL, 2,
5902 G_TYPE_STRING, GST_TYPE_STRUCTURE);
5906 _deref_unparent_and_unref (GObject ** object)
5908 GstObject *obj = GST_OBJECT (*object);
5910 GST_OBJECT_PARENT (obj) = NULL;
5912 gst_object_unref (*object);
5916 _transport_free (GObject ** object)
5918 TransportStream *stream = (TransportStream *) * object;
5919 GstWebRTCBin *webrtc;
5921 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
5923 if (stream->transport) {
5924 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
5925 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
5927 if (stream->rtcp_transport) {
5928 g_signal_handlers_disconnect_by_data (stream->rtcp_transport->transport,
5930 g_signal_handlers_disconnect_by_data (stream->rtcp_transport, webrtc);
5933 gst_object_unref (*object);
5937 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
5939 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
5940 g_mutex_init (PC_GET_LOCK (webrtc));
5941 g_cond_init (PC_GET_COND (webrtc));
5943 webrtc->rtpbin = _create_rtpbin (webrtc);
5944 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
5946 webrtc->priv->transceivers = g_array_new (FALSE, TRUE, sizeof (gpointer));
5947 g_array_set_clear_func (webrtc->priv->transceivers,
5948 (GDestroyNotify) _deref_unparent_and_unref);
5950 webrtc->priv->transports = g_array_new (FALSE, TRUE, sizeof (gpointer));
5951 g_array_set_clear_func (webrtc->priv->transports,
5952 (GDestroyNotify) _transport_free);
5954 webrtc->priv->data_channels = g_array_new (FALSE, TRUE, sizeof (gpointer));
5955 g_array_set_clear_func (webrtc->priv->data_channels,
5956 (GDestroyNotify) _deref_and_unref);
5958 webrtc->priv->pending_data_channels =
5959 g_array_new (FALSE, TRUE, sizeof (gpointer));
5960 g_array_set_clear_func (webrtc->priv->pending_data_channels,
5961 (GDestroyNotify) _deref_and_unref);
5963 webrtc->priv->session_mid_map =
5964 g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
5965 g_array_set_clear_func (webrtc->priv->session_mid_map,
5966 (GDestroyNotify) clear_session_mid_item);
5968 webrtc->priv->ice = gst_webrtc_ice_new ();
5969 g_signal_connect (webrtc->priv->ice, "on-ice-candidate",
5970 G_CALLBACK (_on_ice_candidate), webrtc);
5971 webrtc->priv->ice_stream_map =
5972 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
5973 webrtc->priv->pending_ice_candidates =
5974 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem *));
5975 g_array_set_clear_func (webrtc->priv->pending_ice_candidates,
5976 (GDestroyNotify) _clear_ice_candidate_item);
5978 /* we start off closed until we move to READY */
5979 webrtc->priv->is_closed = TRUE;