2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31 #include "webrtcdatachannel.h"
32 #include "webrtcsctptransport.h"
34 #include "gst/webrtc/webrtc-priv.h"
36 #include <gst/rtp/rtp.h>
42 #define RANDOM_SESSION_ID \
43 ((((((guint64) g_random_int()) << 32) | \
44 (guint64) g_random_int ())) & \
45 G_GUINT64_CONSTANT (0x7fffffffffffffff))
47 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
48 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
49 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
51 #define PC_GET_COND(w) (&w->priv->pc_cond)
52 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
53 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
54 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
56 #define ICE_GET_LOCK(w) (&w->priv->ice_lock)
57 #define ICE_LOCK(w) (g_mutex_lock (ICE_GET_LOCK(w)))
58 #define ICE_UNLOCK(w) (g_mutex_unlock (ICE_GET_LOCK(w)))
60 #define DC_GET_LOCK(w) (&w->priv->dc_lock)
61 #define DC_LOCK(w) (g_mutex_lock (DC_GET_LOCK(w)))
62 #define DC_UNLOCK(w) (g_mutex_unlock (DC_GET_LOCK(w)))
64 /* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */
65 #define RTPSTORAGE_EXTRA_TIME (50)
67 #define DEFAULT_JB_LATENCY 200
69 #define RTPHDREXT_MID GST_RTP_HDREXT_BASE "sdes:mid"
70 #define RTPHDREXT_STREAM_ID GST_RTP_HDREXT_BASE "sdes:rtp-stream-id"
71 #define RTPHDREXT_REPAIRED_STREAM_ID GST_RTP_HDREXT_BASE "sdes:repaired-rtp-stream-id"
74 * SECTION: element-webrtcbin
77 * This webrtcbin implements the majority of the W3's peerconnection API and
78 * implementation guide where possible. Generating offers, answers and setting
79 * local and remote SDP's are all supported. Both media descriptions and
80 * descriptions involving data channels are supported.
82 * Each input/output pad is equivalent to a Track in W3 parlance which are
83 * added/removed from the bin. The number of requested sink pads is the number
84 * of streams that will be sent to the receiver and will be associated with a
85 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
87 * On the receiving side, RTPTransceiver's are created in response to setting
88 * a remote description. Output pads for the receiving streams in the set
89 * description are also created when data is received.
91 * A TransportStream is created when needed in order to transport the data over
92 * the necessary DTLS/ICE channel to the peer. The exact configuration depends
93 * on the negotiated SDP's between the peers based on the bundle and rtcp
94 * configuration. Some cases are outlined below for a simple single
95 * audio/video/data session:
97 * - max-bundle uses a single transport for all
98 * media/data transported. Renegotiation involves adding/removing the
99 * necessary streams to the existing transports.
100 * - max-compat involves two TransportStream per media stream
101 * to transport the rtp and the rtcp packets and a single TransportStream for
102 * all data channels. Each stream change involves modifying the associated
103 * TransportStream/s as necessary.
108 * assert sending payload type matches the stream
109 * reconfiguration (of anything)
111 * balanced bundle policy
112 * setting custom DTLS certificates
114 * separate session id's from mlineindex properly
115 * how to deal with replacing a input/output track/stream
118 static void _update_need_negotiation (GstWebRTCBin * webrtc);
119 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
120 GstWebRTCBinPad * pad);
123 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
124 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
126 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
129 GST_STATIC_CAPS ("application/x-rtp"));
131 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
134 GST_STATIC_CAPS ("application/x-rtp"));
138 PROP_PAD_TRANSCEIVER = 1,
142 _have_nice_elements (GstWebRTCBin * webrtc)
144 GstPluginFeature *feature;
146 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
148 gst_object_unref (feature);
150 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
151 ("%s", "libnice elements are not available"));
155 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
157 gst_object_unref (feature);
159 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
160 ("%s", "libnice elements are not available"));
168 _have_sctp_elements (GstWebRTCBin * webrtc)
170 GstPluginFeature *feature;
172 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
174 gst_object_unref (feature);
176 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
177 ("%s", "sctp elements are not available"));
181 feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
183 gst_object_unref (feature);
185 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
186 ("%s", "sctp elements are not available"));
194 _have_dtls_elements (GstWebRTCBin * webrtc)
196 GstPluginFeature *feature;
198 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
200 gst_object_unref (feature);
202 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
203 ("%s", "dtls elements are not available"));
207 feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
209 gst_object_unref (feature);
211 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
212 ("%s", "dtls elements are not available"));
219 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
222 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
223 GValue * value, GParamSpec * pspec)
225 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
228 case PROP_PAD_TRANSCEIVER:
229 g_value_set_object (value, pad->trans);
232 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
238 gst_webrtc_bin_pad_finalize (GObject * object)
240 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
243 gst_object_unref (pad->trans);
246 if (pad->received_caps)
247 gst_caps_unref (pad->received_caps);
248 pad->received_caps = NULL;
250 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
254 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
256 GObjectClass *gobject_class = (GObjectClass *) klass;
258 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
259 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
261 g_object_class_install_property (gobject_class,
262 PROP_PAD_TRANSCEIVER,
263 g_param_spec_object ("transceiver", "Transceiver",
264 "Transceiver associated with this pad",
265 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
266 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
270 gst_webrtc_bin_pad_update_tos_event (GstWebRTCBinPad * wpad)
272 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
274 if (wpad->received_caps && trans->parent.mid) {
275 GstPad *pad = GST_PAD (wpad);
277 gst_event_take (&trans->tos_event,
278 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
279 gst_structure_new ("GstWebRtcBinUpdateTos", "mid", G_TYPE_STRING,
280 trans->parent.mid, NULL)));
282 GST_DEBUG_OBJECT (pad, "sending new tos event %" GST_PTR_FORMAT,
284 gst_pad_send_event (pad, gst_event_ref (trans->tos_event));
289 _get_pending_sink_transceiver (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
293 for (ret = webrtc->priv->pending_sink_transceivers; ret; ret = ret->next) {
294 if (ret->data == pad)
302 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
304 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
305 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
306 gboolean check_negotiation = FALSE;
308 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
311 gst_event_parse_caps (event, &caps);
312 check_negotiation = (!wpad->received_caps
313 || !gst_caps_is_equal (wpad->received_caps, caps));
314 gst_caps_replace (&wpad->received_caps, caps);
316 GST_DEBUG_OBJECT (parent,
317 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
318 GST_PTR_FORMAT, pad, check_negotiation, caps);
320 if (check_negotiation) {
321 gst_webrtc_bin_pad_update_tos_event (wpad);
324 /* A remote description might have been set while the pad hadn't
325 * yet received caps, delaying the connection of the input stream
329 GST_OBJECT_LOCK (wpad->trans);
330 if (wpad->trans->current_direction ==
331 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY
332 || wpad->trans->current_direction ==
333 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
334 GList *pending = _get_pending_sink_transceiver (webrtc, wpad);
337 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
338 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
339 wpad->trans, wpad->received_caps);
340 _connect_input_stream (webrtc, wpad);
341 gst_pad_remove_probe (GST_PAD (pad), wpad->block_id);
343 gst_object_unref (pending->data);
344 webrtc->priv->pending_sink_transceivers =
345 g_list_delete_link (webrtc->priv->pending_sink_transceivers,
349 GST_OBJECT_UNLOCK (wpad->trans);
352 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
353 check_negotiation = TRUE;
356 if (check_negotiation) {
358 _update_need_negotiation (webrtc);
362 return gst_pad_event_default (pad, parent, event);
366 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
368 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
369 gboolean ret = FALSE;
371 switch (GST_QUERY_TYPE (query)) {
372 case GST_QUERY_ACCEPT_CAPS:
373 GST_OBJECT_LOCK (wpad->trans);
374 if (wpad->trans->codec_preferences) {
377 gst_query_parse_accept_caps (query, &caps);
379 gst_query_set_accept_caps_result (query,
380 gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
383 GST_OBJECT_UNLOCK (wpad->trans);
388 GstCaps *codec_preferences = NULL;
390 GST_OBJECT_LOCK (wpad->trans);
391 if (wpad->trans->codec_preferences)
392 codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
393 GST_OBJECT_UNLOCK (wpad->trans);
395 if (codec_preferences) {
396 GstCaps *filter = NULL;
397 GstCaps *filter_prefs = NULL;
400 gst_query_parse_caps (query, &filter);
403 filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
404 GST_CAPS_INTERSECT_FIRST);
405 gst_caps_unref (codec_preferences);
407 filter_prefs = codec_preferences;
410 target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
414 result = gst_pad_query_caps (target, filter_prefs);
415 gst_query_set_caps_result (query, result);
416 gst_caps_unref (result);
418 gst_object_unref (target);
420 gst_query_set_caps_result (query, filter_prefs);
423 gst_caps_unref (filter_prefs);
435 return gst_pad_query_default (pad, parent, query);
440 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
444 static GstWebRTCBinPad *
445 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
447 GstWebRTCBinPad *pad;
448 GstPadTemplate *template;
450 if (direction == GST_PAD_SINK)
451 template = gst_static_pad_template_get (&sink_template);
452 else if (direction == GST_PAD_SRC)
453 template = gst_static_pad_template_get (&src_template);
455 g_assert_not_reached ();
458 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
459 direction, "template", template, NULL);
460 gst_object_unref (template);
462 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
463 gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
465 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
466 direction == GST_PAD_SRC ? "src" : "sink");
470 #define gst_webrtc_bin_parent_class parent_class
471 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
472 G_ADD_PRIVATE (GstWebRTCBin)
473 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
474 "webrtcbin element"););
480 CREATE_ANSWER_SIGNAL,
481 SET_LOCAL_DESCRIPTION_SIGNAL,
482 SET_REMOTE_DESCRIPTION_SIGNAL,
483 ADD_ICE_CANDIDATE_SIGNAL,
484 ON_NEGOTIATION_NEEDED_SIGNAL,
485 ON_ICE_CANDIDATE_SIGNAL,
486 ON_NEW_TRANSCEIVER_SIGNAL,
488 ADD_TRANSCEIVER_SIGNAL,
489 GET_TRANSCEIVER_SIGNAL,
490 GET_TRANSCEIVERS_SIGNAL,
491 ADD_TURN_SERVER_SIGNAL,
492 CREATE_DATA_CHANNEL_SIGNAL,
493 ON_DATA_CHANNEL_SIGNAL,
494 PREPARE_DATA_CHANNEL_SIGNAL,
501 PROP_CONNECTION_STATE,
502 PROP_SIGNALING_STATE,
503 PROP_ICE_GATHERING_STATE,
504 PROP_ICE_CONNECTION_STATE,
505 PROP_LOCAL_DESCRIPTION,
506 PROP_CURRENT_LOCAL_DESCRIPTION,
507 PROP_PENDING_LOCAL_DESCRIPTION,
508 PROP_REMOTE_DESCRIPTION,
509 PROP_CURRENT_REMOTE_DESCRIPTION,
510 PROP_PENDING_REMOTE_DESCRIPTION,
514 PROP_ICE_TRANSPORT_POLICY,
520 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
525 GstWebRTCICEStream *stream;
528 /* FIXME: locking? */
530 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
534 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
535 IceStreamItem *item =
536 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
538 if (item->session_id == session_id) {
539 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
540 "session %u", item->stream, session_id);
545 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
551 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
552 GstWebRTCICEStream * stream)
554 IceStreamItem item = { session_id, stream };
556 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
557 "session %u", stream, session_id);
558 g_array_append_val (webrtc->priv->ice_stream_map, item);
561 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
564 static GstWebRTCRTPTransceiver *
565 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
566 FindTransceiverFunc func)
570 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
571 GstWebRTCRTPTransceiver *transceiver =
572 g_ptr_array_index (webrtc->priv->transceivers, i);
574 if (func (transceiver, data))
582 transceiver_match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
584 return g_strcmp0 (trans->mid, mid) == 0;
588 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
593 return trans->mline == *mline;
596 static GstWebRTCRTPTransceiver *
597 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
599 GstWebRTCRTPTransceiver *trans;
601 trans = _find_transceiver (webrtc, &mlineindex,
602 (FindTransceiverFunc) transceiver_match_for_mline);
604 GST_TRACE_OBJECT (webrtc,
605 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
611 static GstWebRTCRTPTransceiver *
612 _find_transceiver_for_mid (GstWebRTCBin * webrtc, const char *mid)
614 GstWebRTCRTPTransceiver *trans;
616 trans = _find_transceiver (webrtc, mid,
617 (FindTransceiverFunc) transceiver_match_for_mid);
619 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT " for "
620 "mid %s", trans, mid);
625 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
628 static TransportStream *
629 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
630 FindTransportFunc func)
634 for (i = 0; i < webrtc->priv->transports->len; i++) {
635 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
637 if (func (stream, data))
645 match_stream_for_session (TransportStream * trans, guint * session)
647 return trans->session_id == *session;
650 static TransportStream *
651 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
653 TransportStream *stream;
655 stream = _find_transport (webrtc, &session_id,
656 (FindTransportFunc) match_stream_for_session);
658 GST_TRACE_OBJECT (webrtc,
659 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
664 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
666 static GstWebRTCBinPad *
667 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
669 GstElement *element = GST_ELEMENT (webrtc);
672 GST_OBJECT_LOCK (webrtc);
674 for (; l; l = g_list_next (l)) {
675 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
677 if (func (l->data, data)) {
678 gst_object_ref (l->data);
679 GST_OBJECT_UNLOCK (webrtc);
684 l = webrtc->priv->pending_pads;
685 for (; l; l = g_list_next (l)) {
686 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
688 if (func (l->data, data)) {
689 gst_object_ref (l->data);
690 GST_OBJECT_UNLOCK (webrtc);
694 GST_OBJECT_UNLOCK (webrtc);
699 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
702 static WebRTCDataChannel *
703 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
704 FindDataChannelFunc func)
708 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
709 WebRTCDataChannel *channel =
710 g_ptr_array_index (webrtc->priv->data_channels, i);
712 if (func (channel, data))
720 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
722 return channel->parent.id == *id;
725 /* always called with dc_lock held */
726 static WebRTCDataChannel *
727 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
729 WebRTCDataChannel *channel;
731 channel = _find_data_channel (webrtc, &id,
732 (FindDataChannelFunc) data_channel_match_for_id);
734 GST_TRACE_OBJECT (webrtc,
735 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
741 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
743 GST_OBJECT_LOCK (webrtc);
744 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
745 GST_OBJECT_UNLOCK (webrtc);
749 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
751 gboolean ret = FALSE;
754 GST_OBJECT_LOCK (webrtc);
755 l = g_list_find (webrtc->priv->pending_pads, pad);
757 webrtc->priv->pending_pads =
758 g_list_remove_link (webrtc->priv->pending_pads, l);
762 GST_OBJECT_UNLOCK (webrtc);
768 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
770 _remove_pending_pad (webrtc, pad);
772 if (webrtc->priv->running)
773 gst_pad_set_active (GST_PAD (pad), TRUE);
774 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
778 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
780 _remove_pending_pad (webrtc, pad);
782 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
787 GstPadDirection direction;
792 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
794 return GST_PAD_DIRECTION (pad) == match->direction
795 && pad->trans->mline == match->mline;
798 static GstWebRTCBinPad *
799 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
802 MLineMatch m = { direction, mline };
804 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
809 GstPadDirection direction;
810 GstWebRTCRTPTransceiver *trans;
814 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
816 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
819 static GstWebRTCBinPad *
820 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
821 GstWebRTCRTPTransceiver * trans)
823 TransMatch m = { direction, trans };
825 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
830 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
838 GstWebRTCRTPTransceiverDirection direction;
843 mid_ssrc_match_for_ssrc (SsrcMapItem * entry, const struct SsrcMatch *match)
845 return entry->direction == match->direction && entry->ssrc == match->ssrc;
849 mid_ssrc_remove_ssrc (SsrcMapItem * item, const struct SsrcMatch *match)
851 return !mid_ssrc_match_for_ssrc (item, match);
855 find_mid_ssrc_for_ssrc (GstWebRTCBin * webrtc,
856 GstWebRTCRTPTransceiverDirection direction, guint rtp_session, guint ssrc)
858 TransportStream *stream = _find_transport_for_session (webrtc, rtp_session);
859 struct SsrcMatch m = { direction, ssrc };
864 return transport_stream_find_ssrc_map_item (stream, &m,
865 (FindSsrcMapFunc) mid_ssrc_match_for_ssrc);
869 find_or_add_ssrc_map_item (GstWebRTCBin * webrtc,
870 GstWebRTCRTPTransceiverDirection direction, guint rtp_session, guint ssrc,
873 TransportStream *stream = _find_transport_for_session (webrtc, rtp_session);
874 struct SsrcMatch m = { direction, ssrc };
880 if ((item = transport_stream_find_ssrc_map_item (stream, &m,
881 (FindSsrcMapFunc) mid_ssrc_match_for_ssrc)))
884 return transport_stream_add_ssrc_map_item (stream, direction, ssrc,
889 remove_ssrc_entry_by_ssrc (GstWebRTCBin * webrtc, guint rtp_session, guint ssrc)
891 TransportStream *stream;
893 stream = _find_transport_for_session (webrtc, rtp_session);
896 { GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, ssrc };
898 transport_stream_filter_ssrc_map_item (stream, &m,
899 (FindSsrcMapFunc) mid_ssrc_remove_ssrc);
901 m.direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
902 transport_stream_filter_ssrc_map_item (stream, &m,
903 (FindSsrcMapFunc) mid_ssrc_remove_ssrc);
908 _unlock_pc_thread (GMutex * lock)
910 g_mutex_unlock (lock);
911 return G_SOURCE_REMOVE;
915 _gst_pc_thread (GstWebRTCBin * webrtc)
918 webrtc->priv->main_context = g_main_context_new ();
919 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
921 PC_COND_BROADCAST (webrtc);
922 g_main_context_invoke (webrtc->priv->main_context,
923 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
925 /* Having the thread be the thread default GMainContext will break the
926 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
928 g_main_loop_run (webrtc->priv->loop);
930 GST_OBJECT_LOCK (webrtc);
931 g_main_context_unref (webrtc->priv->main_context);
932 webrtc->priv->main_context = NULL;
933 GST_OBJECT_UNLOCK (webrtc);
936 g_main_loop_unref (webrtc->priv->loop);
937 webrtc->priv->loop = NULL;
938 PC_COND_BROADCAST (webrtc);
945 _start_thread (GstWebRTCBin * webrtc)
950 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
951 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
955 while (!webrtc->priv->loop)
956 PC_COND_WAIT (webrtc);
957 webrtc->priv->is_closed = FALSE;
962 _stop_thread (GstWebRTCBin * webrtc)
964 GST_OBJECT_LOCK (webrtc);
965 webrtc->priv->is_closed = TRUE;
966 GST_OBJECT_UNLOCK (webrtc);
969 g_main_loop_quit (webrtc->priv->loop);
970 while (webrtc->priv->loop)
971 PC_COND_WAIT (webrtc);
974 g_thread_unref (webrtc->priv->thread);
978 _execute_op (GstWebRTCBinTask * op)
982 PC_LOCK (op->webrtc);
983 if (op->webrtc->priv->is_closed) {
984 PC_UNLOCK (op->webrtc);
988 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
989 "webrtcbin is closed. aborting execution.");
990 GstStructure *s = gst_structure_new ("application/x-gst-promise",
991 "error", G_TYPE_ERROR, error, NULL);
993 gst_promise_reply (op->promise, s);
995 g_clear_error (&error);
997 GST_DEBUG_OBJECT (op->webrtc,
998 "Peerconnection is closed, aborting execution");
1002 s = op->op (op->webrtc, op->data);
1004 PC_UNLOCK (op->webrtc);
1007 gst_promise_reply (op->promise, s);
1009 gst_structure_free (s);
1012 return G_SOURCE_REMOVE;
1016 _free_op (GstWebRTCBinTask * op)
1019 op->notify (op->data);
1021 gst_promise_unref (op->promise);
1026 * @promise is for correctly signalling the failure case to the caller when
1027 * the user supplies it. Without passing it in, the promise would never
1028 * be replied to in the case that @webrtc becomes closed between the idle
1029 * source addition and the the execution of the idle source.
1032 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
1033 gpointer data, GDestroyNotify notify, GstPromise * promise)
1035 GstWebRTCBinTask *op;
1039 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
1041 GST_OBJECT_LOCK (webrtc);
1042 if (webrtc->priv->is_closed) {
1043 GST_OBJECT_UNLOCK (webrtc);
1044 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
1049 ctx = g_main_context_ref (webrtc->priv->main_context);
1050 GST_OBJECT_UNLOCK (webrtc);
1052 op = g_new0 (GstWebRTCBinTask, 1);
1053 op->webrtc = webrtc;
1056 op->notify = notify;
1058 op->promise = gst_promise_ref (promise);
1060 source = g_idle_source_new ();
1061 g_source_set_priority (source, G_PRIORITY_DEFAULT);
1062 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
1063 (GDestroyNotify) _free_op);
1064 g_source_attach (source, ctx);
1065 g_source_unref (source);
1066 g_main_context_unref (ctx);
1071 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
1072 static GstWebRTCICEConnectionState
1073 _collate_ice_connection_states (GstWebRTCBin * webrtc)
1075 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
1076 GstWebRTCICEConnectionState any_state = 0;
1077 gboolean all_new_or_closed = TRUE;
1078 gboolean all_completed_or_closed = TRUE;
1079 gboolean all_connected_completed_or_closed = TRUE;
1082 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1083 GstWebRTCRTPTransceiver *rtp_trans =
1084 g_ptr_array_index (webrtc->priv->transceivers, i);
1085 GstWebRTCICETransport *transport;
1086 GstWebRTCICEConnectionState ice_state;
1088 if (rtp_trans->stopped) {
1089 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1093 if (!rtp_trans->mid) {
1094 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1098 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
1100 /* get transport state */
1101 g_object_get (transport, "state", &ice_state, NULL);
1102 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
1104 any_state |= (1 << ice_state);
1106 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
1107 all_new_or_closed = FALSE;
1108 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
1109 all_completed_or_closed = FALSE;
1110 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
1111 && ice_state != STATE (CLOSED))
1112 all_connected_completed_or_closed = FALSE;
1115 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
1117 if (webrtc->priv->is_closed) {
1118 GST_TRACE_OBJECT (webrtc, "returning closed");
1119 return STATE (CLOSED);
1121 /* Any of the RTCIceTransports are in the failed state. */
1122 if (any_state & (1 << STATE (FAILED))) {
1123 GST_TRACE_OBJECT (webrtc, "returning failed");
1124 return STATE (FAILED);
1126 /* Any of the RTCIceTransports are in the disconnected state. */
1127 if (any_state & (1 << STATE (DISCONNECTED))) {
1128 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1129 return STATE (DISCONNECTED);
1131 /* All of the RTCIceTransports are in the new or closed state, or there are
1133 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
1134 GST_TRACE_OBJECT (webrtc, "returning new");
1137 /* Any of the RTCIceTransports are in the checking or new state. */
1138 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
1139 GST_TRACE_OBJECT (webrtc, "returning checking");
1140 return STATE (CHECKING);
1142 /* All RTCIceTransports are in the completed or closed state. */
1143 if (all_completed_or_closed) {
1144 GST_TRACE_OBJECT (webrtc, "returning completed");
1145 return STATE (COMPLETED);
1147 /* All RTCIceTransports are in the connected, completed or closed state. */
1148 if (all_connected_completed_or_closed) {
1149 GST_TRACE_OBJECT (webrtc, "returning connected");
1150 return STATE (CONNECTED);
1153 GST_FIXME ("unspecified situation, returning old state");
1154 return webrtc->ice_connection_state;
1158 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1159 static GstWebRTCICEGatheringState
1160 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1162 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1163 GstWebRTCICEGatheringState any_state = 0;
1164 GstWebRTCICEGatheringState ice_state;
1165 GstWebRTCDTLSTransport *dtls_transport;
1166 GstWebRTCICETransport *transport;
1167 gboolean all_completed = webrtc->priv->transceivers->len > 0 ||
1168 webrtc->priv->data_channel_transport;
1171 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1172 GstWebRTCRTPTransceiver *rtp_trans =
1173 g_ptr_array_index (webrtc->priv->transceivers, i);
1174 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1175 TransportStream *stream = trans->stream;
1177 if (rtp_trans->stopped || stream == NULL) {
1178 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1183 /* We only have a mid in the transceiver after we got the SDP answer,
1184 * which is usually long after gathering has finished */
1185 if (!rtp_trans->mid) {
1186 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1189 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1190 if (dtls_transport == NULL) {
1191 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1195 transport = dtls_transport->transport;
1197 /* get gathering state */
1198 g_object_get (transport, "gathering-state", &ice_state, NULL);
1199 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1201 any_state |= (1 << ice_state);
1202 if (ice_state != STATE (COMPLETE))
1203 all_completed = FALSE;
1206 /* check data channel transport gathering state */
1207 if (all_completed && webrtc->priv->data_channel_transport) {
1208 if ((dtls_transport = webrtc->priv->data_channel_transport->transport)) {
1209 transport = dtls_transport->transport;
1210 g_object_get (transport, "gathering-state", &ice_state, NULL);
1211 GST_TRACE_OBJECT (webrtc,
1212 "data channel transport %p gathering state: 0x%x", dtls_transport,
1214 any_state |= (1 << ice_state);
1215 if (ice_state != STATE (COMPLETE))
1216 all_completed = FALSE;
1220 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1222 /* Any of the RTCIceTransport s are in the gathering state. */
1223 if (any_state & (1 << STATE (GATHERING))) {
1224 GST_TRACE_OBJECT (webrtc, "returning gathering");
1225 return STATE (GATHERING);
1227 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1228 * the completed gathering state. */
1229 if (all_completed) {
1230 GST_TRACE_OBJECT (webrtc, "returning complete");
1231 return STATE (COMPLETE);
1234 /* Any of the RTCIceTransport s are in the new gathering state and none
1235 * of the transports are in the gathering state, or there are no transports. */
1236 GST_TRACE_OBJECT (webrtc, "returning new");
1241 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1242 static GstWebRTCPeerConnectionState
1243 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1245 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1246 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1247 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1248 GstWebRTCICEConnectionState any_ice_state = 0;
1249 GstWebRTCDTLSTransportState any_dtls_state = 0;
1250 gboolean ice_all_new_or_closed = TRUE;
1251 gboolean dtls_all_new_or_closed = TRUE;
1252 gboolean ice_all_new_connecting_or_checking = TRUE;
1253 gboolean dtls_all_new_connecting_or_checking = TRUE;
1254 gboolean ice_all_connected_completed_or_closed = TRUE;
1255 gboolean dtls_all_connected_completed_or_closed = TRUE;
1258 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1259 GstWebRTCRTPTransceiver *rtp_trans =
1260 g_ptr_array_index (webrtc->priv->transceivers, i);
1261 GstWebRTCDTLSTransport *transport;
1262 GstWebRTCICEConnectionState ice_state;
1263 GstWebRTCDTLSTransportState dtls_state;
1265 if (rtp_trans->stopped) {
1266 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1269 if (!rtp_trans->mid) {
1270 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1274 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1276 /* get transport state */
1277 g_object_get (transport, "state", &dtls_state, NULL);
1278 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1280 any_dtls_state |= (1 << dtls_state);
1282 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1283 dtls_all_new_or_closed = FALSE;
1284 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1285 dtls_all_new_connecting_or_checking = FALSE;
1286 if (dtls_state != DTLS_STATE (CONNECTED)
1287 && dtls_state != DTLS_STATE (CLOSED))
1288 dtls_all_connected_completed_or_closed = FALSE;
1290 g_object_get (transport->transport, "state", &ice_state, NULL);
1291 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1293 any_ice_state |= (1 << ice_state);
1295 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1296 ice_all_new_or_closed = FALSE;
1297 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1298 ice_all_new_connecting_or_checking = FALSE;
1299 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1300 && ice_state != ICE_STATE (CLOSED))
1301 ice_all_connected_completed_or_closed = FALSE;
1304 // also check data channel transport state
1305 if (webrtc->priv->data_channel_transport) {
1306 GstWebRTCDTLSTransport *transport =
1307 webrtc->priv->data_channel_transport->transport;
1308 GstWebRTCICEConnectionState ice_state;
1309 GstWebRTCDTLSTransportState dtls_state;
1311 g_object_get (transport, "state", &dtls_state, NULL);
1312 GST_TRACE_OBJECT (webrtc, "data channel transport DTLS state: 0x%x",
1314 any_dtls_state |= (1 << dtls_state);
1316 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1317 dtls_all_new_or_closed = FALSE;
1318 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1319 dtls_all_new_connecting_or_checking = FALSE;
1320 if (dtls_state != DTLS_STATE (CONNECTED)
1321 && dtls_state != DTLS_STATE (CLOSED))
1322 dtls_all_connected_completed_or_closed = FALSE;
1324 g_object_get (transport->transport, "state", &ice_state, NULL);
1325 GST_TRACE_OBJECT (webrtc, "data channel transport ICE state: 0x%x",
1327 any_ice_state |= (1 << ice_state);
1329 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1330 ice_all_new_or_closed = FALSE;
1331 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1332 ice_all_new_connecting_or_checking = FALSE;
1333 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1334 && ice_state != ICE_STATE (CLOSED))
1335 ice_all_connected_completed_or_closed = FALSE;
1338 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1339 "state: 0x%x", any_ice_state, any_dtls_state);
1341 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1342 if (webrtc->priv->is_closed) {
1343 GST_TRACE_OBJECT (webrtc, "returning closed");
1344 return STATE (CLOSED);
1347 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1348 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1349 GST_TRACE_OBJECT (webrtc, "returning failed");
1350 return STATE (FAILED);
1352 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1353 GST_TRACE_OBJECT (webrtc, "returning failed");
1354 return STATE (FAILED);
1357 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1359 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1360 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1361 return STATE (DISCONNECTED);
1364 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1365 * state, or there are no transports. */
1366 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1367 || webrtc->priv->transports->len == 0) {
1368 GST_TRACE_OBJECT (webrtc, "returning new");
1372 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1373 * or checking state. */
1374 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1375 GST_TRACE_OBJECT (webrtc, "returning connecting");
1376 return STATE (CONNECTING);
1379 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1380 * completed or closed state. */
1381 if (dtls_all_connected_completed_or_closed
1382 && ice_all_connected_completed_or_closed) {
1383 GST_TRACE_OBJECT (webrtc, "returning connected");
1384 return STATE (CONNECTED);
1387 /* FIXME: Unspecified state that happens for us */
1388 if ((dtls_all_new_connecting_or_checking
1389 || dtls_all_connected_completed_or_closed)
1390 && (ice_all_new_connecting_or_checking
1391 || ice_all_connected_completed_or_closed)) {
1392 GST_TRACE_OBJECT (webrtc, "returning connecting");
1393 return STATE (CONNECTING);
1396 GST_FIXME_OBJECT (webrtc,
1397 "Undefined situation detected, returning old state");
1398 return webrtc->peer_connection_state;
1404 static GstStructure *
1405 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1407 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1408 GstWebRTCICEGatheringState new_state;
1410 new_state = _collate_ice_gathering_states (webrtc);
1412 /* If the new state is complete, before we update the public state,
1413 * check if anyone published more ICE candidates while we were collating
1414 * and stop if so, because it means there's a new later
1415 * ice_gathering_state_task queued */
1416 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1418 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1419 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1420 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1422 ICE_UNLOCK (webrtc);
1425 if (new_state != webrtc->ice_gathering_state) {
1426 gchar *old_s, *new_s;
1428 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1430 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1432 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1433 old_s, old_state, new_s, new_state);
1437 webrtc->ice_gathering_state = new_state;
1439 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1447 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1449 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1453 static GstStructure *
1454 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1456 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1457 GstWebRTCICEConnectionState new_state;
1459 new_state = _collate_ice_connection_states (webrtc);
1461 if (new_state != old_state) {
1462 gchar *old_s, *new_s;
1464 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1466 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1468 GST_INFO_OBJECT (webrtc,
1469 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1474 webrtc->ice_connection_state = new_state;
1476 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1484 _update_ice_connection_state (GstWebRTCBin * webrtc)
1486 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1490 static GstStructure *
1491 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1493 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1494 GstWebRTCPeerConnectionState new_state;
1496 new_state = _collate_peer_connection_states (webrtc);
1498 if (new_state != old_state) {
1499 gchar *old_s, *new_s;
1501 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1503 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1505 GST_INFO_OBJECT (webrtc,
1506 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1511 webrtc->peer_connection_state = new_state;
1513 g_object_notify (G_OBJECT (webrtc), "connection-state");
1521 _update_peer_connection_state (GstWebRTCBin * webrtc)
1523 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1528 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1531 gboolean res = FALSE;
1533 GST_OBJECT_LOCK (webrtc);
1534 l = GST_ELEMENT (webrtc)->pads;
1535 for (; l; l = g_list_next (l)) {
1536 GstWebRTCBinPad *wpad;
1538 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1541 wpad = GST_WEBRTC_BIN_PAD (l->data);
1542 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1543 && (!wpad->trans || !wpad->trans->stopped)) {
1544 if (wpad->trans && wpad->trans->codec_preferences) {
1552 l = webrtc->priv->pending_pads;
1553 for (; l; l = g_list_next (l)) {
1554 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1562 GST_OBJECT_UNLOCK (webrtc);
1566 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1568 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1572 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1574 /* We can't negotiate until we have received caps on all our sink pads,
1575 * as we will need the formats in our offer / answer */
1576 if (!_all_sinks_have_caps (webrtc)) {
1577 GST_LOG_OBJECT (webrtc,
1578 "no negotiation possible until caps have been received on all sink pads");
1582 /* If any implementation-specific negotiation is required, as described at
1583 * the start of this section, return "true".
1585 /* FIXME: emit when input caps/format changes? */
1587 if (!webrtc->current_local_description) {
1588 GST_LOG_OBJECT (webrtc, "no local description set");
1592 if (!webrtc->current_remote_description) {
1593 GST_LOG_OBJECT (webrtc, "no remote description set");
1597 /* If connection has created any RTCDataChannel's, and no m= section has
1598 * been negotiated yet for data, return "true". */
1599 if (webrtc->priv->data_channels->len > 0) {
1600 if (_message_get_datachannel_index (webrtc->current_local_description->
1601 sdp) >= G_MAXUINT) {
1602 GST_LOG_OBJECT (webrtc,
1603 "no data channel media section and have %u " "transports",
1604 webrtc->priv->data_channels->len);
1609 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1610 GstWebRTCRTPTransceiver *trans;
1612 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1614 if (trans->stopped) {
1615 /* FIXME: If t is stopped and is associated with an m= section according to
1616 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1617 * rejected in connection's currentLocalDescription or
1618 * currentRemoteDescription , return "true". */
1619 GST_FIXME_OBJECT (webrtc,
1620 "check if the transceiver is rejected in descriptions");
1622 const GstSDPMedia *media;
1623 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1625 if (trans->mline == -1 || trans->mid == NULL) {
1626 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1627 " mid %s", i, trans, trans->mid);
1630 /* internal inconsistency */
1631 g_assert (trans->mline <
1632 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1633 g_assert (trans->mline <
1634 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1636 /* FIXME: msid handling
1637 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1638 * section in connection's currentLocalDescription doesn't contain an
1639 * "a=msid" line, return "true". */
1642 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1644 local_dir = _get_direction_from_media (media);
1647 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1649 remote_dir = _get_direction_from_media (media);
1651 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1652 /* If connection's currentLocalDescription if of type "offer", and
1653 * the direction of the associated m= section in neither the offer
1654 * nor answer matches t's direction, return "true". */
1656 if (local_dir != trans->direction && remote_dir != trans->direction) {
1657 gchar *local_str, *remote_str, *dir_str;
1660 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1663 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1666 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1669 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1670 "description (local %s remote %s)", dir_str, local_str,
1675 g_free (remote_str);
1679 } else if (webrtc->current_local_description->type ==
1680 GST_WEBRTC_SDP_TYPE_ANSWER) {
1681 GstWebRTCRTPTransceiverDirection intersect_dir;
1683 /* If connection's currentLocalDescription if of type "answer", and
1684 * the direction of the associated m= section in the answer does not
1685 * match t's direction intersected with the offered direction (as
1686 * described in [JSEP] (section 5.3.1.)), return "true". */
1688 /* remote is the offer, local is the answer */
1689 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1691 if (intersect_dir != trans->direction) {
1692 gchar *local_str, *remote_str, *inter_str, *dir_str;
1695 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1698 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1701 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1704 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1707 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1708 "description intersected direction %s (local %s remote %s)",
1709 dir_str, local_str, inter_str, remote_str);
1713 g_free (remote_str);
1722 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1726 static GstStructure *
1727 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1729 if (webrtc->priv->need_negotiation) {
1730 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1732 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1740 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1742 _update_need_negotiation (GstWebRTCBin * webrtc)
1744 /* If connection's [[isClosed]] slot is true, abort these steps. */
1745 if (webrtc->priv->is_closed)
1747 /* If connection's signaling state is not "stable", abort these steps. */
1748 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1751 /* If the result of checking if negotiation is needed is "false", clear the
1752 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1753 * to false, and abort these steps. */
1754 if (!_check_if_negotiation_is_needed (webrtc)) {
1755 webrtc->priv->need_negotiation = FALSE;
1758 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1759 if (webrtc->priv->need_negotiation)
1761 /* Set connection's [[needNegotiation]] slot to true. */
1762 webrtc->priv->need_negotiation = TRUE;
1763 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1764 * true, fire a simple event named negotiationneeded at connection. */
1765 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1770 _query_pad_caps (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * rtp_trans,
1771 GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
1776 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1777 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1779 /* Only return an error if actual empty caps were returned from the query. */
1780 if (gst_caps_is_empty (caps)) {
1781 g_set_error (error, GST_WEBRTC_ERROR,
1782 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
1783 "Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
1784 gst_clear_caps (&caps);
1785 gst_caps_unref (filter);
1789 n = gst_caps_get_size (caps);
1791 /* Make sure the caps are complete enough to figure out the media type and
1792 * encoding-name, otherwise they would match with basically any media. */
1793 caps = gst_caps_make_writable (caps);
1794 for (i = n; i > 0; i--) {
1795 const GstStructure *s = gst_caps_get_structure (caps, i - 1);
1797 if (!gst_structure_has_name (s, "application/x-rtp") ||
1798 !gst_structure_has_field (s, "media") ||
1799 !gst_structure_has_field (s, "encoding-name")) {
1800 gst_caps_remove_structure (caps, i - 1);
1805 /* If the filtering above resulted in empty caps, or the caps were ANY to
1806 * begin with, then don't report and error but just NULL.
1808 * This would be the case if negotiation would not fail but the peer does
1809 * not have any specific enough preferred caps that would allow us to
1812 if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
1813 GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
1814 gst_clear_caps (&caps);
1817 gst_caps_unref (filter);
1823 _find_codec_preferences (GstWebRTCBin * webrtc,
1824 GstWebRTCRTPTransceiver * rtp_trans, guint media_idx, GError ** error)
1826 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1827 GstCaps *ret = NULL;
1828 GstCaps *codec_preferences = NULL;
1829 GstWebRTCBinPad *pad = NULL;
1830 GstPadDirection direction;
1832 g_assert (rtp_trans);
1833 g_assert (error && *error == NULL);
1835 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1838 GST_OBJECT_LOCK (rtp_trans);
1839 if (rtp_trans->codec_preferences) {
1840 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1841 rtp_trans->codec_preferences);
1842 codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
1844 GST_OBJECT_UNLOCK (rtp_trans);
1846 if (rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
1847 direction = GST_PAD_SRC;
1849 direction = GST_PAD_SINK;
1851 pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
1853 /* try to find a pad */
1855 pad = _find_pad_for_mline (webrtc, direction, media_idx);
1857 /* For the case where we have set our transceiver to sendrecv, but the
1858 * sink pad has not been requested yet.
1861 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1863 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1865 /* try to find a pad */
1867 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
1871 GstCaps *caps = NULL;
1873 if (pad->received_caps) {
1874 caps = gst_caps_ref (pad->received_caps);
1876 static GstStaticCaps static_filter =
1877 GST_STATIC_CAPS ("application/x-rtp, "
1878 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1879 GstCaps *filter = gst_static_caps_get (&static_filter);
1881 filter = gst_caps_make_writable (filter);
1883 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
1884 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
1885 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
1886 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
1888 caps = _query_pad_caps (webrtc, rtp_trans, pad, filter, error);
1895 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1896 GstWebRTCBinPad *srcpad =
1897 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1900 caps = _query_pad_caps (webrtc, rtp_trans, srcpad, caps, error);
1901 gst_object_unref (srcpad);
1908 if (caps && codec_preferences) {
1909 GstCaps *intersection;
1911 intersection = gst_caps_intersect_full (codec_preferences, caps,
1912 GST_CAPS_INTERSECT_FIRST);
1913 gst_clear_caps (&caps);
1915 if (gst_caps_is_empty (intersection)) {
1916 g_set_error (error, GST_WEBRTC_ERROR,
1917 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
1918 "Caps negotiation on pad %s failed against codec preferences",
1919 GST_PAD_NAME (pad));
1920 gst_clear_caps (&intersection);
1922 caps = intersection;
1928 gst_caps_replace (&trans->last_retrieved_caps, caps);
1935 if (codec_preferences)
1936 ret = gst_caps_ref (codec_preferences);
1937 else if (trans->last_retrieved_caps)
1938 ret = gst_caps_ref (trans->last_retrieved_caps);
1944 gst_object_unref (pad);
1945 if (codec_preferences)
1946 gst_caps_unref (codec_preferences);
1949 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1955 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1956 WebRTCTransceiver * trans, const GstCaps * caps)
1965 ret = gst_caps_make_writable (caps);
1967 kind = webrtc_kind_from_caps (ret);
1968 for (i = 0; i < gst_caps_get_size (ret); i++) {
1969 GstStructure *s = gst_caps_get_structure (ret, i);
1972 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1973 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1975 if (kind == GST_WEBRTC_KIND_VIDEO
1976 && !gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1977 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1978 if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1979 gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
1981 /* FIXME: codec-specific parameters? */
1988 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1989 GParamSpec * pspec, GstWebRTCBin * webrtc)
1991 _update_ice_connection_state (webrtc);
1992 _update_peer_connection_state (webrtc);
1996 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1997 GParamSpec * pspec, GstWebRTCBin * webrtc)
1999 _update_ice_gathering_state (webrtc);
2003 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
2004 GParamSpec * pspec, GstWebRTCBin * webrtc)
2006 _update_peer_connection_state (webrtc);
2010 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
2011 gboolean early, gpointer user_data)
2013 GstWebRTCBin *webrtc = user_data;
2014 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
2015 GstRTCPPacket packet;
2017 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
2020 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
2021 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
2023 GstWebRTCRTPTransceiver *rtp_trans = NULL;
2024 WebRTCTransceiver *trans;
2028 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
2031 GPOINTER_TO_UINT (g_object_get_data (internal_session,
2032 "GstWebRTCBinRTPSessionID"));
2034 mid = find_mid_ssrc_for_ssrc (webrtc,
2035 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session, ssrc);
2036 if (mid && mid->mid) {
2037 rtp_trans = _find_transceiver_for_mid (webrtc, mid->mid);
2038 GST_LOG_OBJECT (webrtc, "found %" GST_PTR_FORMAT " from mid entry "
2039 "using rtp session %u ssrc %u -> mid \'%s\'", rtp_trans,
2040 rtp_session, ssrc, mid->mid);
2042 trans = (WebRTCTransceiver *) rtp_trans;
2044 if (rtp_trans && rtp_trans->sender && trans->tos_event) {
2046 gchar *pad_name = NULL;
2049 g_strdup_printf ("send_rtcp_src_%u",
2050 rtp_trans->sender->transport->session_id);
2051 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
2054 gst_pad_push_event (pad, gst_event_ref (trans->tos_event));
2055 gst_object_unref (pad);
2061 gst_rtcp_buffer_unmap (&rtcp);
2064 /* False means we don't care about suppression */
2069 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
2071 GObject *internal_session = NULL;
2073 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
2074 session_id, &internal_session);
2076 if (internal_session) {
2077 g_object_set_data (internal_session, "GstWebRTCBinRTPSessionID",
2078 GUINT_TO_POINTER (session_id));
2079 g_signal_connect (internal_session, "on-sending-rtcp",
2080 G_CALLBACK (_on_sending_rtcp), webrtc);
2081 g_object_unref (internal_session);
2086 weak_free (GWeakRef * weak)
2088 g_weak_ref_clear (weak);
2092 static GstPadProbeReturn
2093 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
2095 GstWebRTCBin *webrtc = g_weak_ref_get ((GWeakRef *) user_data);
2098 return GST_PAD_PROBE_REMOVE;
2100 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
2101 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
2102 const GstStructure *s =
2103 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
2105 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
2109 if ((mid = gst_structure_get_string (s, "mid"))) {
2110 GstWebRTCRTPTransceiver *rtp_trans;
2112 rtp_trans = _find_transceiver_for_mid (webrtc, mid);
2114 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
2115 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
2116 trans->stream->session_id);
2119 /* Set DSCP field based on
2120 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2122 switch (rtp_trans->sender->priority) {
2123 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2126 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2129 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2130 switch (rtp_trans->kind) {
2131 case GST_WEBRTC_KIND_AUDIO:
2134 case GST_WEBRTC_KIND_VIDEO:
2135 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
2137 case GST_WEBRTC_KIND_UNKNOWN:
2142 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2143 switch (rtp_trans->kind) {
2144 case GST_WEBRTC_KIND_AUDIO:
2147 case GST_WEBRTC_KIND_VIDEO:
2148 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
2150 case GST_WEBRTC_KIND_UNKNOWN:
2157 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
2159 } else if (gst_structure_get_enum (s, "sctp-priority",
2160 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
2163 /* Set DSCP field based on
2164 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2167 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2170 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2173 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2174 dscp = 10; /* AF11 */
2176 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2177 dscp = 18; /* AF21 */
2180 if (webrtc->priv->data_channel_transport)
2181 gst_webrtc_ice_set_tos (webrtc->priv->ice,
2182 webrtc->priv->data_channel_transport->stream, dscp << 2);
2187 gst_object_unref (webrtc);
2189 return GST_PAD_PROBE_OK;
2192 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
2195 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
2197 GstWebRTCPriorityType sctp_priority = 0;
2200 if (!webrtc->priv->sctp_transport)
2204 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2205 GstWebRTCDataChannel *channel
2206 = g_ptr_array_index (webrtc->priv->data_channels, i);
2208 sctp_priority = MAX (sctp_priority, channel->priority);
2212 /* Default priority is low means DSCP field is left as 0 */
2213 if (sctp_priority == 0)
2214 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
2216 /* Nobody asks for DSCP, leave it as-is */
2217 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
2218 !webrtc->priv->tos_attached)
2221 /* If one stream has a non-default priority, then everyone else does too */
2222 gst_webrtc_bin_attach_tos (webrtc);
2224 webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
2229 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
2230 GstWebRTCICETransport * transport)
2235 pad = gst_element_get_static_pad (transport->sink, "sink");
2237 weak = g_new0 (GWeakRef, 1);
2238 g_weak_ref_init (weak, webrtc);
2240 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2241 _nicesink_pad_probe, weak, (GDestroyNotify) weak_free);
2242 gst_object_unref (pad);
2246 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
2250 if (webrtc->priv->tos_attached)
2252 webrtc->priv->tos_attached = TRUE;
2254 for (i = 0; i < webrtc->priv->transports->len; i++) {
2255 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
2257 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
2259 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
2260 stream->transport->transport);
2263 gst_webrtc_bin_update_sctp_priority (webrtc);
2266 static WebRTCTransceiver *
2267 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
2268 GstWebRTCRTPTransceiverDirection direction, guint mline, GstWebRTCKind kind,
2269 GstCaps * codec_preferences)
2271 char *dir_str = gst_webrtc_rtp_transceiver_direction_to_string (direction);
2272 WebRTCTransceiver *trans;
2273 GstWebRTCRTPTransceiver *rtp_trans;
2274 GstWebRTCRTPSender *sender;
2275 GstWebRTCRTPReceiver *receiver;
2277 sender = gst_webrtc_rtp_sender_new ();
2278 receiver = gst_webrtc_rtp_receiver_new ();
2279 trans = webrtc_transceiver_new (webrtc, sender, receiver);
2280 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2281 rtp_trans->direction = direction;
2282 rtp_trans->mline = mline;
2283 rtp_trans->kind = kind;
2284 rtp_trans->codec_preferences =
2285 codec_preferences ? gst_caps_ref (codec_preferences) : NULL;
2286 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
2287 rtp_trans->stopped = FALSE;
2289 GST_LOG_OBJECT (webrtc, "created new transceiver %" GST_PTR_FORMAT " with "
2290 "direction %s (%d), mline %u, kind %s (%d)", rtp_trans, dir_str,
2291 direction, mline, gst_webrtc_kind_to_string (kind), kind);
2293 g_signal_connect_object (sender, "notify::priority",
2294 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
2296 g_ptr_array_add (webrtc->priv->transceivers, trans);
2298 gst_object_unref (sender);
2299 gst_object_unref (receiver);
2301 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
2309 static TransportStream *
2310 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2312 GstWebRTCDTLSTransport *transport;
2313 TransportStream *ret;
2316 /* FIXME: how to parametrize the sender and the receiver */
2317 ret = transport_stream_new (webrtc, session_id);
2318 transport = ret->transport;
2320 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2321 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2322 g_signal_connect (G_OBJECT (transport->transport),
2323 "notify::gathering-state",
2324 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2325 g_signal_connect (G_OBJECT (transport), "notify::state",
2326 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2327 if (webrtc->priv->tos_attached)
2328 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2330 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2331 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2332 g_ptr_array_add (webrtc->priv->transports, ret);
2334 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2335 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2336 GST_ELEMENT (webrtc->rtpbin), pad_name))
2337 g_warn_if_reached ();
2340 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2341 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2342 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2343 g_warn_if_reached ();
2346 GST_TRACE_OBJECT (webrtc,
2347 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2352 static TransportStream *
2353 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2355 TransportStream *ret;
2357 ret = _find_transport_for_session (webrtc, session_id);
2360 ret = _create_transport_channel (webrtc, session_id);
2362 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2363 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2368 /* this is called from the webrtc thread with the pc lock held */
2370 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2371 GParamSpec * pspec, GstWebRTCBin * webrtc)
2373 GstWebRTCDataChannelState ready_state;
2375 g_object_get (channel, "ready-state", &ready_state, NULL);
2377 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2381 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2382 if (found == FALSE) {
2383 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2388 g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2391 gst_webrtc_bin_update_sctp_priority (webrtc);
2393 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2395 } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2399 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2400 || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2402 if (found == FALSE) {
2403 GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2410 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2411 GstWebRTCBin * webrtc)
2413 WebRTCDataChannel *channel;
2417 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2421 channel = _find_data_channel_for_id (webrtc, stream_id);
2423 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2424 channel->parent.id = stream_id;
2425 channel->webrtcbin = webrtc;
2427 g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL],
2430 gst_bin_add (GST_BIN (webrtc), channel->appsrc);
2431 gst_bin_add (GST_BIN (webrtc), channel->appsink);
2433 gst_element_sync_state_with_parent (channel->appsrc);
2434 gst_element_sync_state_with_parent (channel->appsink);
2436 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2438 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2442 g_signal_connect (channel, "notify::ready-state",
2443 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2445 sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2446 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2447 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2448 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2449 gst_object_unref (sink_pad);
2453 _on_sctp_state_notify (WebRTCSCTPTransport * sctp, GParamSpec * pspec,
2454 GstWebRTCBin * webrtc)
2456 GstWebRTCSCTPTransportState state;
2458 g_object_get (sctp, "state", &state, NULL);
2460 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2463 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2466 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2467 WebRTCDataChannel *channel;
2469 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2471 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2473 if (!channel->parent.negotiated && !channel->opened)
2474 webrtc_data_channel_start_negotiation (channel);
2480 /* Forward declaration so we can easily disconnect the signal handler */
2481 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2482 GParamSpec * pspec, GstWebRTCBin * webrtc);
2484 static GstStructure *
2485 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2487 TransportStream *stream;
2488 GstWebRTCDTLSTransport *transport;
2489 GstWebRTCDTLSTransportState dtls_state;
2490 WebRTCSCTPTransport *sctp_transport;
2492 stream = webrtc->priv->data_channel_transport;
2493 transport = stream->transport;
2495 g_object_get (transport, "state", &dtls_state, NULL);
2496 /* Not connected yet so just return */
2497 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2498 GST_DEBUG_OBJECT (webrtc,
2499 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2503 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2504 sctp_transport = webrtc->priv->sctp_transport;
2506 /* Not locked state anymore so this was already taken care of before */
2507 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2510 /* Start up the SCTP elements now that the DTLS connection is established */
2511 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2512 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2514 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2515 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2517 if (sctp_transport->sctpdec_block_id) {
2518 GstPad *receive_srcpad;
2521 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2523 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2525 sctp_transport->sctpdec_block_id = 0;
2526 gst_object_unref (receive_srcpad);
2529 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2536 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2537 GParamSpec * pspec, GstWebRTCBin * webrtc)
2539 GstWebRTCDTLSTransportState dtls_state;
2541 g_object_get (transport, "state", &dtls_state, NULL);
2543 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2546 /* Connected now, so schedule a task to update the state of the SCTP
2548 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2549 gst_webrtc_bin_enqueue_task (webrtc,
2550 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2554 static GstPadProbeReturn
2555 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2557 /* Drop all events: we don't care about them and don't want to block on
2558 * them. Sticky events would be forwarded again later once we unblock
2559 * and we don't want to forward them here already because that might
2560 * cause a spurious GST_FLOW_FLUSHING */
2561 if (GST_IS_EVENT (info->data))
2562 return GST_PAD_PROBE_DROP;
2564 /* But block on any actual data-flow so we don't accidentally send that
2565 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2568 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2570 return GST_PAD_PROBE_OK;
2573 static TransportStream *
2574 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2576 if (!webrtc->priv->data_channel_transport) {
2577 TransportStream *stream;
2578 WebRTCSCTPTransport *sctp_transport;
2580 stream = _find_transport_for_session (webrtc, session_id);
2583 stream = _create_transport_channel (webrtc, session_id);
2585 webrtc->priv->data_channel_transport = stream;
2587 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2588 sctp_transport = webrtc_sctp_transport_new ();
2589 sctp_transport->transport =
2590 g_object_ref (webrtc->priv->data_channel_transport->transport);
2591 sctp_transport->webrtcbin = webrtc;
2593 /* Don't automatically start SCTP elements as part of webrtcbin. We
2594 * need to delay this until the DTLS transport is fully connected! */
2595 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2596 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2598 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2599 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2602 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2603 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2604 g_signal_connect (sctp_transport, "notify::state",
2605 G_CALLBACK (_on_sctp_state_notify), webrtc);
2607 if (sctp_transport->sctpdec_block_id == 0) {
2608 GstPad *receive_srcpad;
2610 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2612 sctp_transport->sctpdec_block_id =
2613 gst_pad_add_probe (receive_srcpad,
2614 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2615 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2616 gst_object_unref (receive_srcpad);
2619 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2620 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2621 g_warn_if_reached ();
2623 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2624 GST_ELEMENT (stream->send_bin), "data_sink"))
2625 g_warn_if_reached ();
2627 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2628 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2630 if (!webrtc->priv->sctp_transport) {
2631 /* Connect to the notify::state signal to get notified when the DTLS
2632 * connection is established. Only then can we start the SCTP elements */
2633 g_signal_connect (stream->transport, "notify::state",
2634 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2636 /* As this would be racy otherwise, also schedule a task that checks the
2637 * current state of the connection already without getting the signal
2639 gst_webrtc_bin_enqueue_task (webrtc,
2640 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2643 webrtc->priv->sctp_transport = sctp_transport;
2645 gst_webrtc_bin_update_sctp_priority (webrtc);
2648 return webrtc->priv->data_channel_transport;
2651 static TransportStream *
2652 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2653 gboolean is_datachannel)
2656 return _get_or_create_data_channel_transports (webrtc, session_id);
2658 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2661 struct media_payload_map_item
2671 media_payload_map_item_init (struct media_payload_map_item *item,
2674 item->media_pt = media_pt;
2675 item->red_pt = G_MAXUINT;
2676 item->rtx_pt = G_MAXUINT;
2677 item->ulpfec_pt = G_MAXUINT;
2678 item->red_rtx_pt = G_MAXUINT;
2681 static struct media_payload_map_item *
2682 find_payload_map_for_media_pt (GArray * media_mapping, guint media_pt)
2686 for (i = 0; i < media_mapping->len; i++) {
2687 struct media_payload_map_item *item;
2689 item = &g_array_index (media_mapping, struct media_payload_map_item, i);
2691 if (item->media_pt == media_pt)
2698 static struct media_payload_map_item *
2699 find_or_create_payload_map_for_media_pt (GArray * media_mapping, guint media_pt)
2701 struct media_payload_map_item new_item;
2702 struct media_payload_map_item *item;
2704 if ((item = find_payload_map_for_media_pt (media_mapping, media_pt)))
2707 media_payload_map_item_init (&new_item, media_pt);
2708 g_array_append_val (media_mapping, new_item);
2709 return &g_array_index (media_mapping, struct media_payload_map_item,
2710 media_mapping->len - 1);
2714 _pick_available_pt (GArray * media_mapping, guint * ret)
2718 for (i = 96; i <= 127; i++) {
2721 for (j = 0; j < media_mapping->len; j++) {
2722 struct media_payload_map_item *item;
2724 item = &g_array_index (media_mapping, struct media_payload_map_item, j);
2725 if (item->media_pt == i)
2727 if (item->red_pt == i)
2729 if (item->rtx_pt == i)
2731 if (item->ulpfec_pt == i)
2733 if (item->red_rtx_pt == i)
2746 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2747 GArray * media_mapping, gint clockrate, gint media_pt, gint * rtx_target_pt,
2748 GstSDPMedia * media)
2750 gboolean ret = TRUE;
2752 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2755 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2756 struct media_payload_map_item *item;
2759 item = find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
2760 if (item->red_pt == G_MAXUINT) {
2761 if (!(ret = _pick_available_pt (media_mapping, &item->red_pt)))
2765 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2767 str = g_strdup_printf ("%u", item->red_pt);
2768 gst_sdp_media_add_format (media, str);
2770 str = g_strdup_printf ("%u red/%d", item->red_pt, clockrate);
2771 gst_sdp_media_add_attribute (media, "rtpmap", str);
2774 *rtx_target_pt = item->red_pt;
2776 if (item->ulpfec_pt == G_MAXUINT) {
2777 if (!(ret = _pick_available_pt (media_mapping, &item->ulpfec_pt)))
2781 str = g_strdup_printf ("%u", item->ulpfec_pt);
2782 gst_sdp_media_add_format (media, str);
2784 str = g_strdup_printf ("%u ulpfec/%d", item->ulpfec_pt, clockrate);
2785 gst_sdp_media_add_attribute (media, "rtpmap", str);
2794 add_rtx_to_media (WebRTCTransceiver * trans, gint clockrate, gint rtx_pt,
2795 gint rtx_target_pt, guint target_ssrc, GstSDPMedia * media)
2799 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2800 if (target_ssrc != -1) {
2801 str = g_strdup_printf ("%u", target_ssrc);
2802 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2803 g_random_int (), NULL);
2807 str = g_strdup_printf ("%u", rtx_pt);
2808 gst_sdp_media_add_format (media, str);
2811 str = g_strdup_printf ("%u rtx/%d", rtx_pt, clockrate);
2812 gst_sdp_media_add_attribute (media, "rtpmap", str);
2815 str = g_strdup_printf ("%u apt=%d", rtx_pt, rtx_target_pt);
2816 gst_sdp_media_add_attribute (media, "fmtp", str);
2821 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2822 GArray * media_mapping, gint clockrate, gint media_pt, gint target_pt,
2823 guint target_ssrc, GstSDPMedia * media)
2825 gboolean ret = TRUE;
2827 if (trans->local_rtx_ssrc_map)
2828 gst_structure_free (trans->local_rtx_ssrc_map);
2830 trans->local_rtx_ssrc_map =
2831 gst_structure_new_empty ("application/x-rtp-ssrc-map");
2833 if (trans->do_nack) {
2834 struct media_payload_map_item *item;
2836 item = find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
2837 if (item->rtx_pt == G_MAXUINT) {
2838 if (!(ret = _pick_available_pt (media_mapping, &item->rtx_pt)))
2842 add_rtx_to_media (trans, clockrate, item->rtx_pt, media_pt, target_ssrc,
2845 if (item->red_pt != G_MAXUINT) {
2846 /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2847 if (item->red_rtx_pt == G_MAXUINT) {
2848 if (!(ret = _pick_available_pt (media_mapping, &item->red_rtx_pt)))
2851 add_rtx_to_media (trans, clockrate, item->red_rtx_pt, item->red_pt,
2852 target_ssrc, media);
2860 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2862 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2863 GstSDPMedia * media)
2868 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2869 g_value_get_uint (value));
2870 gst_sdp_media_add_attribute (media, "ssrc-group", str);
2880 GstWebRTCBin *webrtc;
2881 WebRTCTransceiver *trans;
2885 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2891 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2892 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2893 cname = gst_structure_get_string (sdes, "cname");
2895 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2897 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2898 cname, GST_OBJECT_NAME (data->trans));
2899 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2902 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2903 gst_sdp_media_add_attribute (data->media, "ssrc", str);
2906 gst_structure_free (sdes);
2912 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2913 WebRTCTransceiver * trans)
2916 RtxSsrcData data = { media, webrtc, trans };
2920 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2921 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2922 cname = gst_structure_get_string (sdes, "cname");
2924 if (trans->local_rtx_ssrc_map)
2925 gst_structure_foreach (trans->local_rtx_ssrc_map,
2926 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2928 for (i = 0; i < gst_caps_get_size (caps); i++) {
2929 const GstStructure *s = gst_caps_get_structure (caps, i);
2932 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2935 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2937 g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2938 GST_OBJECT_NAME (trans));
2939 gst_sdp_media_add_attribute (media, "ssrc", str);
2942 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2943 gst_sdp_media_add_attribute (media, "ssrc", str);
2948 gst_structure_free (sdes);
2950 if (trans->local_rtx_ssrc_map)
2951 gst_structure_foreach (trans->local_rtx_ssrc_map,
2952 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2956 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2957 GstSDPMedia * media)
2959 gchar *cert, *fingerprint, *val;
2961 g_object_get (transport, "certificate", &cert, NULL);
2964 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2967 g_strdup_printf ("%s %s",
2968 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2969 g_free (fingerprint);
2971 gst_sdp_media_add_attribute (media, "fingerprint", val);
2976 _parse_extmap (GQuark field_id, const GValue * value, GError ** error)
2980 if (G_VALUE_HOLDS_STRING (value)) {
2981 ret = g_value_dup_string (value);
2982 } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
2983 && gst_value_array_get_size (value) == 3) {
2985 const gchar *direction, *extensionname, *extensionattributes;
2987 val = gst_value_array_get_value (value, 0);
2988 direction = g_value_get_string (val);
2990 val = gst_value_array_get_value (value, 1);
2991 extensionname = g_value_get_string (val);
2993 val = gst_value_array_get_value (value, 2);
2994 extensionattributes = g_value_get_string (val);
2996 if (!extensionname || *extensionname == '\0')
2999 if (direction && *direction != '\0' && extensionattributes
3000 && *extensionattributes != '\0') {
3002 g_strdup_printf ("/%s %s %s", direction, extensionname,
3003 extensionattributes);
3004 } else if (direction && *direction != '\0') {
3005 ret = g_strdup_printf ("/%s %s", direction, extensionname);
3006 } else if (extensionattributes && *extensionattributes != '\0') {
3007 ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
3009 ret = g_strdup (extensionname);
3013 if (!ret && error) {
3014 gchar *val_str = gst_value_serialize (value);
3016 g_set_error (error, GST_WEBRTC_ERROR,
3017 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3018 "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
3029 GstStructure *extmap;
3034 _dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
3036 gboolean is_extmap =
3037 g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
3043 gchar *new_value = _parse_extmap (field_id, value, data->error);
3050 if (gst_structure_id_has_field (data->extmap, field_id)) {
3052 _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
3055 g_assert (old_value);
3057 if (g_strcmp0 (new_value, old_value)) {
3059 ("extmap contains different values for id %s (%s != %s)",
3060 g_quark_to_string (field_id), old_value, new_value);
3061 g_set_error (data->error, GST_WEBRTC_ERROR,
3062 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3063 "extmap contains different values for id %s (%s != %s)",
3064 g_quark_to_string (field_id), old_value, new_value);
3073 gst_structure_id_set_value (data->extmap, field_id, value);
3083 static GstStructure *
3084 _gather_extmap (GstCaps * caps, GError ** error)
3087 { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
3090 n = gst_caps_get_size (caps);
3092 for (i = 0; i < n; i++) {
3093 GstStructure *s = gst_caps_get_structure (caps, i);
3095 gst_structure_filter_and_map_in_place (s,
3096 (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
3099 gst_clear_structure (&edata.extmap);
3104 return edata.extmap;
3109 const char *rtphdrext_uri;
3114 structure_value_get_rtphdrext_id (GQuark field_id, const GValue * value,
3117 struct hdrext_id *rtphdrext = user_data;
3118 const char *field_name = g_quark_to_string (field_id);
3120 if (g_str_has_prefix (field_name, "extmap-")) {
3121 const char *val = NULL;
3123 if (GST_VALUE_HOLDS_ARRAY (value) && gst_value_array_get_size (value) >= 2) {
3124 value = gst_value_array_get_value (value, 1);
3126 if (G_VALUE_HOLDS_STRING (value)) {
3127 val = g_value_get_string (value);
3130 if (g_strcmp0 (val, rtphdrext->rtphdrext_uri) == 0) {
3131 gint64 id = g_ascii_strtoll (&field_name[strlen ("extmap-")], NULL, 10);
3133 if (id > 0 && id < 256)
3134 rtphdrext->ext_id = id;
3143 // Returns -1 when not found
3145 caps_get_rtp_header_extension_id (const GstCaps * caps,
3146 const char *rtphdrext_uri)
3150 n = gst_caps_get_size (caps);
3151 for (i = 0; i < n; i++) {
3152 const GstStructure *s = gst_caps_get_structure (caps, i);
3153 struct hdrext_id data = { rtphdrext_uri, -1 };
3155 gst_structure_foreach (s, structure_value_get_rtphdrext_id, &data);
3157 if (data.ext_id != -1)
3165 caps_contain_rtp_header_extension (const GstCaps * caps,
3166 const char *rtphdrext_uri)
3168 return caps_get_rtp_header_extension_id (caps, rtphdrext_uri) != -1;
3172 _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
3174 gst_structure_id_set_value (s, field_id, value);
3179 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
3181 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
3182 const GstSDPMedia * last_media, GstWebRTCRTPTransceiver * trans,
3183 guint media_idx, GString * bundled_mids, guint bundle_idx,
3184 gchar * bundle_ufrag, gchar * bundle_pwd, GArray * media_mapping,
3185 GHashTable * all_mids, GError ** error)
3188 * rtp header extensions
3195 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
3197 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3198 gchar *direction, *ufrag, *pwd, *mid;
3199 gboolean bundle_only;
3200 guint rtp_session_idx;
3202 GstStructure *extmap;
3205 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3208 g_assert (trans->mline == -1 || trans->mline == media_idx);
3210 rtp_session_idx = bundled_mids ? bundle_idx : media_idx;
3212 bundle_only = bundled_mids && bundle_idx != media_idx
3213 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
3215 caps = _find_codec_preferences (webrtc, trans, media_idx, error);
3216 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3219 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
3220 gst_clear_caps (&caps);
3225 n = gst_sdp_media_formats_len (last_media);
3227 caps = gst_caps_new_empty ();
3228 for (i = 0; i < n; i++) {
3229 guint fmt = atoi (gst_sdp_media_get_format (last_media, i));
3230 GstCaps *tmp = gst_sdp_media_get_caps_from_media (last_media, fmt);
3231 GstStructure *s = gst_caps_get_structure (tmp, 0);
3232 gst_structure_set_name (s, "application/x-rtp");
3233 gst_caps_append_structure (caps, gst_structure_copy (s));
3234 gst_clear_caps (&tmp);
3236 GST_DEBUG_OBJECT (webrtc, "using previously negotiated caps for "
3237 "transceiver %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, trans, caps);
3242 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver %"
3243 GST_PTR_FORMAT ", skipping", trans);
3249 const char *setup = gst_sdp_media_get_attribute_val (last_media, "setup");
3251 gst_sdp_media_add_attribute (media, "setup", setup);
3253 g_set_error (error, GST_WEBRTC_ERROR,
3254 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3255 "media %u cannot renegotiate without an existing a=setup line",
3260 /* mandated by JSEP */
3261 gst_sdp_media_add_attribute (media, "setup", "actpass");
3264 /* FIXME: deal with ICE restarts */
3265 if (last_offer && trans->mline != -1 && trans->mid) {
3266 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
3267 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
3268 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
3270 GST_DEBUG_OBJECT (trans,
3271 "%u Generating new ice parameters mline %i, mid %s", media_idx,
3272 trans->mline, trans->mid);
3273 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3274 _generate_ice_credentials (&ufrag, &pwd);
3276 g_assert (bundle_ufrag && bundle_pwd);
3277 ufrag = g_strdup (bundle_ufrag);
3278 pwd = g_strdup (bundle_pwd);
3282 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3283 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3287 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
3288 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3289 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3292 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3295 /* FIXME: negotiate this */
3296 /* FIXME: when bundle_only, these should not be added:
3297 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
3298 * However, this causes incompatibilities with current versions
3299 * of the major browsers */
3300 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
3301 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
3304 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
3306 gst_sdp_media_add_attribute (media, direction, "");
3309 caps = gst_caps_make_writable (caps);
3311 /* When an extmap is defined twice for the same ID, firefox complains and
3312 * errors out (chrome is smart enough to accept strict duplicates).
3314 * To work around this, we deduplicate extmap attributes, and also error
3315 * out when a different extmap is defined for the same ID.
3317 * _gather_extmap will strip out all extmap- fields, which will then be
3318 * added upon adding the first format for the media.
3320 extmap = _gather_extmap (caps, error);
3323 GST_ERROR_OBJECT (webrtc,
3324 "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
3325 gst_caps_unref (caps);
3329 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3332 for (i = 0; i < gst_caps_get_size (caps); i++) {
3333 GstCaps *format = gst_caps_new_empty ();
3334 GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
3337 gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
3340 gst_caps_append_structure (format, s);
3342 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
3343 " to %u-th media", i, format, media_idx);
3345 /* this only looks at the first structure so we loop over the given caps
3346 * and add each structure inside it piecemeal */
3347 gst_sdp_media_set_media_from_caps (format, media);
3349 gst_caps_unref (format);
3352 gst_clear_structure (&extmap);
3355 const GstStructure *s = gst_caps_get_structure (caps, 0);
3356 gint clockrate = -1;
3358 guint rtx_target_ssrc = -1;
3361 if (gst_structure_get_int (s, "payload", &media_pt) &&
3362 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3363 find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
3365 rtx_target_pt = media_pt;
3367 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
3368 GST_WARNING_OBJECT (webrtc,
3369 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
3370 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc)) {
3371 if (!caps_contain_rtp_header_extension (caps, RTPHDREXT_MID)) {
3372 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
3377 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), media_mapping,
3378 clockrate, media_pt, &rtx_target_pt, media);
3379 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), media_mapping,
3380 clockrate, media_pt, rtx_target_pt, rtx_target_ssrc, media);
3383 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
3385 /* Some identifier; we also add the media name to it so it's identifiable */
3387 const char *media_mid = gst_sdp_media_get_attribute_val (media, "mid");
3390 gst_sdp_media_add_attribute (media, "mid", trans->mid);
3391 } else if (g_strcmp0 (media_mid, trans->mid) != 0) {
3392 g_set_error (error, GST_WEBRTC_ERROR,
3393 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3394 "Cannot change media %u mid value from \'%s\' to \'%s\'",
3395 media_idx, media_mid, trans->mid);
3398 mid = g_strdup (trans->mid);
3400 const GstStructure *s = gst_caps_get_structure (caps, 0);
3402 mid = g_strdup (gst_structure_get_string (s, "a-mid"));
3405 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3406 g_set_error (error, GST_WEBRTC_ERROR,
3407 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3408 "Cannot re-use mid \'%s\' from the caps in m= line %u that has "
3409 "already been used for a previous m= line in the SDP", mid,
3413 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3415 /* Make sure to avoid mid collisions */
3417 mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3418 webrtc->priv->media_counter++);
3419 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3422 gst_sdp_media_add_attribute (media, "mid", mid);
3423 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3431 * - add a=candidate lines for gathered candidates
3434 if (trans->sender) {
3435 if (!trans->sender->transport) {
3436 TransportStream *item;
3438 item = _get_or_create_transport_stream (webrtc, rtp_session_idx, FALSE);
3440 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
3443 _add_fingerprint_to_media (trans->sender->transport, media);
3448 g_string_append_printf (bundled_mids, " %s", mid);
3451 g_clear_pointer (&mid, g_free);
3453 gst_caps_unref (caps);
3459 gather_pad_pt (GstWebRTCBinPad * pad, GArray * media_mapping)
3461 if (pad->received_caps) {
3462 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
3465 if (gst_structure_get_int (s, "payload", &pt)) {
3466 GST_TRACE_OBJECT (pad, "have media pt %u from received caps", pt);
3467 find_or_create_payload_map_for_media_pt (media_mapping, pt);
3473 gather_media_mapping (GstWebRTCBin * webrtc)
3475 GstElement *element = GST_ELEMENT (webrtc);
3476 GArray *media_mapping =
3477 g_array_new (FALSE, FALSE, sizeof (struct media_payload_map_item));
3480 GST_OBJECT_LOCK (webrtc);
3481 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, media_mapping);
3482 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
3485 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3486 GstWebRTCRTPTransceiver *trans;
3488 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3489 GST_OBJECT_LOCK (trans);
3490 if (trans->codec_preferences) {
3494 n = gst_caps_get_size (trans->codec_preferences);
3495 for (j = 0; j < n; j++) {
3496 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
3497 if (gst_structure_get_int (s, "payload", &pt)) {
3498 GST_TRACE_OBJECT (trans, "have media pt %u from codec preferences",
3500 find_or_create_payload_map_for_media_pt (media_mapping, pt);
3504 GST_OBJECT_UNLOCK (trans);
3506 GST_OBJECT_UNLOCK (webrtc);
3508 return media_mapping;
3512 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
3513 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
3514 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
3516 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3517 gchar *ufrag, *pwd, *sdp_mid;
3518 gboolean bundle_only = bundled_mids
3519 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
3520 && gst_sdp_message_medias_len (msg) != bundle_idx;
3521 guint last_data_index = G_MAXUINT;
3523 /* add data channel support */
3524 if (webrtc->priv->data_channels->len == 0)
3528 last_data_index = _message_get_datachannel_index (last_offer);
3529 if (last_data_index < G_MAXUINT) {
3530 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
3531 /* XXX: is this always true when recycling transceivers?
3532 * i.e. do we always put the data channel in the same mline */
3533 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
3537 /* mandated by JSEP */
3538 gst_sdp_media_add_attribute (media, "setup", "actpass");
3540 /* FIXME: only needed when restarting ICE */
3541 if (last_offer && last_data_index < G_MAXUINT) {
3542 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
3543 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
3545 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3546 _generate_ice_credentials (&ufrag, &pwd);
3548 ufrag = g_strdup (bundle_ufrag);
3549 pwd = g_strdup (bundle_pwd);
3552 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3553 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3557 gst_sdp_media_set_media (media, "application");
3558 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
3559 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3560 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3561 gst_sdp_media_add_format (media, "webrtc-datachannel");
3563 if (bundle_idx != gst_sdp_message_medias_len (msg))
3564 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3566 if (last_offer && last_data_index < G_MAXUINT) {
3567 const GstSDPMedia *last_data_media;
3570 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
3571 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
3573 gst_sdp_media_add_attribute (media, "mid", mid);
3575 /* Make sure to avoid mid collisions */
3577 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3578 webrtc->priv->media_counter++);
3579 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3582 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3583 g_hash_table_insert (all_mids, sdp_mid, NULL);
3590 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3593 g_string_append_printf (bundled_mids, " %s", mid);
3596 /* FIXME: negotiate this properly */
3597 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3599 _get_or_create_data_channel_transports (webrtc,
3600 bundled_mids ? 0 : webrtc->priv->transceivers->len);
3601 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
3606 /* TODO: use the options argument */
3607 static GstSDPMessage *
3608 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3611 GstSDPMessage *ret = NULL;
3612 GString *bundled_mids = NULL;
3613 gchar *bundle_ufrag = NULL;
3614 gchar *bundle_pwd = NULL;
3615 GArray *media_mapping = NULL;
3616 GHashTable *all_mids =
3617 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3619 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3620 GList *seen_transceivers = NULL;
3621 guint media_idx = 0;
3624 gst_sdp_message_new (&ret);
3626 gst_sdp_message_set_version (ret, "0");
3629 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
3631 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
3632 sess_id = g_strdup (origin->sess_id);
3634 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
3636 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
3640 gst_sdp_message_set_session_name (ret, "-");
3641 gst_sdp_message_add_time (ret, "0", "0", NULL);
3642 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
3644 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
3645 bundled_mids = g_string_new ("BUNDLE");
3646 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
3647 bundled_mids = g_string_new ("BUNDLE");
3650 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3651 GStrv last_bundle = NULL;
3652 guint bundle_media_index;
3654 media_mapping = gather_media_mapping (webrtc);
3655 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
3656 && last_bundle && last_bundle[0]
3657 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
3659 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
3661 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
3663 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3666 g_strfreev (last_bundle);
3669 /* FIXME: recycle transceivers */
3671 /* Fill up the renegotiated streams first */
3673 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
3674 GstWebRTCRTPTransceiver *trans = NULL;
3675 const GstSDPMedia *last_media;
3677 last_media = gst_sdp_message_get_media (last_offer, i);
3679 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
3680 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
3681 const gchar *last_mid;
3683 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
3685 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3686 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3688 if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
3689 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
3693 memset (&media, 0, sizeof (media));
3695 g_assert (!g_list_find (seen_transceivers, trans));
3697 if (wtrans->mline_locked && trans->mline != media_idx) {
3698 g_set_error (error, GST_WEBRTC_ERROR,
3699 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3700 "Previous negotiatied transceiver <%s> with mid %s was in "
3701 "mline %d but transceiver has locked mline %u",
3702 GST_OBJECT_NAME (trans), trans->mid, media_idx, trans->mline);
3706 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3707 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3708 trans->mid, media_idx);
3710 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3712 g_array_new (FALSE, FALSE,
3713 sizeof (struct media_payload_map_item));
3716 gst_sdp_media_init (&media);
3717 if (!sdp_media_from_transceiver (webrtc, &media, last_media, trans,
3718 media_idx, bundled_mids, 0, bundle_ufrag, bundle_pwd,
3719 media_mapping, all_mids, error)) {
3720 gst_sdp_media_uninit (&media);
3722 g_set_error_literal (error, GST_WEBRTC_ERROR,
3723 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3724 "Could not reuse transceiver");
3727 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3728 g_array_free (media_mapping, TRUE);
3729 media_mapping = NULL;
3734 mid = gst_sdp_media_get_attribute_val (&media, "mid");
3735 g_assert (mid && g_strcmp0 (last_mid, mid) == 0);
3737 gst_sdp_message_add_media (ret, &media);
3740 gst_sdp_media_uninit (&media);
3741 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3745 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3746 "application") == 0) {
3747 GstSDPMedia media = { 0, };
3748 gst_sdp_media_init (&media);
3749 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3750 bundle_ufrag, bundle_pwd, all_mids)) {
3751 gst_sdp_message_add_media (ret, &media);
3754 gst_sdp_media_uninit (&media);
3760 /* First, go over all transceivers and gather existing mids */
3761 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3762 GstWebRTCRTPTransceiver *trans;
3764 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3766 if (g_list_find (seen_transceivers, trans))
3770 if (g_hash_table_contains (all_mids, trans->mid)) {
3771 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3772 "Duplicate mid %s when creating offer", trans->mid);
3776 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3781 /* add any extra streams */
3783 GstWebRTCRTPTransceiver *trans = NULL;
3784 GstSDPMedia media = { 0, };
3786 /* First find a transceiver requesting this m-line */
3787 trans = _find_transceiver_for_mline (webrtc, media_idx);
3790 /* We can't have seen it already, because it is locked to this line */
3791 g_assert (!g_list_find (seen_transceivers, trans));
3792 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3794 /* Otherwise find a free transceiver */
3795 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3796 WebRTCTransceiver *wtrans;
3798 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3799 wtrans = WEBRTC_TRANSCEIVER (trans);
3801 /* don't add transceivers twice */
3802 if (g_list_find (seen_transceivers, trans))
3805 /* Ignore transceivers with a locked mline, as they would have been
3806 * found above or will be used later */
3807 if (wtrans->mline_locked)
3810 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3811 /* don't add stopped transceivers */
3812 if (trans->stopped) {
3816 /* Otherwise take it */
3820 /* Stop if we got all transceivers */
3821 if (i == webrtc->priv->transceivers->len) {
3823 /* But try to add a data channel first, we do it here, because
3824 * it can allow a locked m-line to be put after, so we need to
3825 * do another iteration after.
3827 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3828 GstSDPMedia media = { 0, };
3829 gst_sdp_media_init (&media);
3830 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3831 bundle_ufrag, bundle_pwd, all_mids)) {
3832 gst_sdp_message_add_media (ret, &media);
3836 gst_sdp_media_uninit (&media);
3840 /* Verify that we didn't ignore any locked m-line transceivers */
3841 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3842 WebRTCTransceiver *wtrans;
3844 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3845 wtrans = WEBRTC_TRANSCEIVER (trans);
3846 /* don't add transceivers twice */
3847 if (g_list_find (seen_transceivers, trans))
3849 g_assert (wtrans->mline_locked);
3851 g_set_error (error, GST_WEBRTC_ERROR,
3852 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3853 "Tranceiver <%s> with mid %s has locked mline %d but the offer "
3854 "only has %u sections", GST_OBJECT_NAME (trans), trans->mid,
3855 trans->mline, media_idx);
3862 gst_sdp_media_init (&media);
3864 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3866 g_array_new (FALSE, FALSE, sizeof (struct media_payload_map_item));
3869 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3870 "index %u", trans, media_idx);
3872 if (sdp_media_from_transceiver (webrtc, &media, NULL, trans, media_idx,
3873 bundled_mids, 0, bundle_ufrag, bundle_pwd, media_mapping, all_mids,
3875 /* as per JSEP, a=rtcp-mux-only is only added for new streams */
3876 gst_sdp_media_add_attribute (&media, "rtcp-mux-only", "");
3877 gst_sdp_message_add_media (ret, &media);
3880 gst_sdp_media_uninit (&media);
3883 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3884 g_array_free (media_mapping, TRUE);
3885 media_mapping = NULL;
3891 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3892 g_array_free (media_mapping, TRUE);
3893 media_mapping = NULL;
3896 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
3899 g_assert (media_idx == gst_sdp_message_medias_len (ret));
3902 gchar *mids = g_string_free (bundled_mids, FALSE);
3904 gst_sdp_message_add_attribute (ret, "group", mids);
3906 bundled_mids = NULL;
3909 /* FIXME: pre-emptively setup receiving elements when needed */
3911 if (webrtc->priv->last_generated_answer)
3912 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3913 webrtc->priv->last_generated_answer = NULL;
3914 if (webrtc->priv->last_generated_offer)
3915 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3917 GstSDPMessage *copy;
3918 gst_sdp_message_copy (ret, ©);
3919 webrtc->priv->last_generated_offer =
3920 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3925 g_array_free (media_mapping, TRUE);
3927 g_hash_table_unref (all_mids);
3929 g_list_free (seen_transceivers);
3932 g_free (bundle_ufrag);
3935 g_free (bundle_pwd);
3938 g_string_free (bundled_mids, TRUE);
3943 gst_sdp_message_free (ret);
3949 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3950 gint * rtx_target_pt)
3954 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3957 for (i = 0; i < gst_caps_get_size (caps); i++) {
3958 const GstStructure *s = gst_caps_get_structure (caps, i);
3960 if (gst_structure_has_name (s, "application/x-rtp")) {
3961 const gchar *encoding_name =
3962 gst_structure_get_string (s, "encoding-name");
3966 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3967 gst_structure_get_int (s, "payload", &pt)) {
3968 if (!g_strcmp0 (encoding_name, "RED")) {
3971 str = g_strdup_printf ("%u", pt);
3972 gst_sdp_media_add_format (media, str);
3974 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3975 *rtx_target_pt = pt;
3976 gst_sdp_media_add_attribute (media, "rtpmap", str);
3978 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3981 str = g_strdup_printf ("%u", pt);
3982 gst_sdp_media_add_format (media, str);
3984 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3985 gst_sdp_media_add_attribute (media, "rtpmap", str);
3994 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3995 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3998 const GstStructure *s;
4000 if (trans->local_rtx_ssrc_map)
4001 gst_structure_free (trans->local_rtx_ssrc_map);
4003 trans->local_rtx_ssrc_map =
4004 gst_structure_new_empty ("application/x-rtp-ssrc-map");
4006 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
4007 s = gst_caps_get_structure (offer_caps, i);
4009 if (gst_structure_has_name (s, "application/x-rtp")) {
4010 const gchar *encoding_name =
4011 gst_structure_get_string (s, "encoding-name");
4012 const gchar *apt_str = gst_structure_get_string (s, "apt");
4020 apt = atoi (apt_str);
4022 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
4023 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
4024 if (!g_strcmp0 (encoding_name, "RTX")) {
4027 str = g_strdup_printf ("%u", pt);
4028 gst_sdp_media_add_format (media, str);
4030 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
4031 gst_sdp_media_add_attribute (media, "rtpmap", str);
4034 str = g_strdup_printf ("%d apt=%d", pt, apt);
4035 gst_sdp_media_add_attribute (media, "fmtp", str);
4038 str = g_strdup_printf ("%u", target_ssrc);
4039 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
4040 g_random_int (), NULL);
4049 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
4050 const GstCaps * caps)
4052 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
4054 if (trans->kind == kind)
4057 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
4066 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
4067 guint * target_ssrc)
4069 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
4071 gst_structure_get_int (s, "payload", target_pt);
4072 gst_structure_get_uint (s, "ssrc", target_ssrc);
4075 /* TODO: use the options argument */
4076 static GstSDPMessage *
4077 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
4080 GstSDPMessage *ret = NULL;
4081 const GstWebRTCSessionDescription *pending_remote =
4082 webrtc->pending_remote_description;
4084 GStrv bundled = NULL;
4085 guint bundle_idx = 0;
4086 GString *bundled_mids = NULL;
4087 gchar *bundle_ufrag = NULL;
4088 gchar *bundle_pwd = NULL;
4089 GList *seen_transceivers = NULL;
4090 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
4092 if (!webrtc->pending_remote_description) {
4093 g_set_error_literal (error, GST_WEBRTC_ERROR,
4094 GST_WEBRTC_ERROR_INVALID_STATE,
4095 "Asked to create an answer without a remote description");
4099 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
4103 GStrv last_bundle = NULL;
4104 guint bundle_media_index;
4106 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
4107 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
4108 "Bundle tag is %s but no media found matching", bundled[0]);
4112 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
4113 bundled_mids = g_string_new ("BUNDLE");
4116 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
4117 && last_bundle && last_bundle[0]
4118 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
4120 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
4122 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
4124 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
4127 g_strfreev (last_bundle);
4130 gst_sdp_message_new (&ret);
4132 gst_sdp_message_set_version (ret, "0");
4134 const GstSDPOrigin *offer_origin =
4135 gst_sdp_message_get_origin (pending_remote->sdp);
4136 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
4137 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
4139 gst_sdp_message_set_session_name (ret, "-");
4141 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
4142 const GstSDPAttribute *attr =
4143 gst_sdp_message_get_attribute (pending_remote->sdp, i);
4145 if (g_strcmp0 (attr->key, "ice-options") == 0) {
4146 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
4150 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
4151 GstSDPMedia *media = NULL;
4152 GstSDPMedia *offer_media;
4153 GstWebRTCDTLSSetup offer_setup, answer_setup;
4155 gboolean bundle_only;
4159 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
4160 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
4162 gst_sdp_media_new (&media);
4163 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
4164 gst_sdp_media_set_port_info (media, 0, 0);
4166 gst_sdp_media_set_port_info (media, 9, 0);
4167 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
4172 /* FIXME: deal with ICE restarts */
4173 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
4174 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
4175 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
4178 _generate_ice_credentials (&ufrag, &pwd);
4180 ufrag = g_strdup (bundle_ufrag);
4181 pwd = g_strdup (bundle_pwd);
4184 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
4185 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
4190 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
4191 const GstSDPAttribute *attr =
4192 gst_sdp_media_get_attribute (offer_media, j);
4194 if (g_strcmp0 (attr->key, "mid") == 0
4195 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
4196 gst_sdp_media_add_attribute (media, attr->key, attr->value);
4197 /* FIXME: handle anything we want to keep */
4201 mid = gst_sdp_media_get_attribute_val (media, "mid");
4202 /* XXX: not strictly required but a lot of functionality requires a mid */
4205 /* set the a=setup: attribute */
4206 offer_setup = _get_dtls_setup_from_media (offer_media);
4207 answer_setup = _intersect_dtls_setup (offer_setup);
4208 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4209 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
4210 "transceiver direction");
4213 _media_replace_setup (media, answer_setup);
4215 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
4218 if (gst_sdp_media_formats_len (offer_media) != 1) {
4219 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
4220 "for webrtc-datachannel");
4223 sctp_port = _get_sctp_port_from_media (offer_media);
4224 if (sctp_port == -1) {
4225 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
4229 /* XXX: older browsers will produce a different SDP format for data
4230 * channel that is currently not parsed correctly */
4231 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
4233 gst_sdp_media_set_media (media, "application");
4234 gst_sdp_media_set_port_info (media, 9, 0);
4235 gst_sdp_media_add_format (media, "webrtc-datachannel");
4237 /* FIXME: negotiate this properly on renegotiation */
4238 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
4240 _get_or_create_data_channel_transports (webrtc,
4241 bundled_mids ? bundle_idx : i);
4245 g_string_append_printf (bundled_mids, " %s", mid);
4248 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
4250 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
4251 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
4252 GstCaps *offer_caps, *answer_caps = NULL;
4253 GstWebRTCRTPTransceiver *rtp_trans = NULL;
4254 WebRTCTransceiver *trans = NULL;
4255 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
4256 gint target_pt = -1;
4257 gint original_target_pt = -1;
4258 guint target_ssrc = 0;
4260 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
4261 offer_caps = _rtp_caps_from_media (offer_media);
4263 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
4264 && (rtp_trans = _find_transceiver_for_mid (webrtc, mid))) {
4265 const GstSDPMedia *last_media =
4266 gst_sdp_message_get_media (last_answer, i);
4267 const gchar *last_mid =
4268 gst_sdp_media_get_attribute_val (last_media, "mid");
4269 GstCaps *current_caps;
4271 /* FIXME: assumes no shenanigans with recycling transceivers */
4272 g_assert (g_strcmp0 (mid, last_mid) == 0);
4274 current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4276 gst_caps_unref (offer_caps);
4280 current_caps = _rtp_caps_from_media (last_media);
4283 answer_caps = gst_caps_intersect (offer_caps, current_caps);
4284 if (gst_caps_is_empty (answer_caps)) {
4285 GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
4286 GST_PTR_FORMAT ") don't intersect with caps from codec"
4287 " preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
4289 gst_caps_unref (current_caps);
4290 gst_caps_unref (answer_caps);
4291 gst_caps_unref (offer_caps);
4294 gst_caps_unref (current_caps);
4297 /* XXX: In theory we're meant to use the sendrecv formats for the
4298 * inactive direction however we don't know what that may be and would
4299 * require asking outside what it expects to possibly send later */
4301 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
4302 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
4303 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
4305 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
4306 GstCaps *trans_caps;
4308 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
4310 if (g_list_find (seen_transceivers, rtp_trans)) {
4311 /* Don't double allocate a transceiver to multiple mlines */
4316 trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
4318 gst_caps_unref (offer_caps);
4322 GST_LOG_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4323 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4325 /* FIXME: technically this is a little overreaching as some fields we
4326 * we can deal with not having and/or we may have unrecognized fields
4327 * that we cannot actually support */
4329 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4330 gst_caps_unref (trans_caps);
4332 if (!gst_caps_is_empty (answer_caps)) {
4333 GST_LOG_OBJECT (webrtc,
4334 "found compatible transceiver %" GST_PTR_FORMAT
4335 " for offer media %u", rtp_trans, i);
4338 gst_caps_unref (answer_caps);
4347 answer_dir = rtp_trans->direction;
4348 g_assert (answer_caps != NULL);
4350 /* if no transceiver, then we only receive that stream and respond with
4351 * the intersection with the transceivers codec preferences caps */
4352 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
4353 GST_WARNING_OBJECT (webrtc, "did not find compatible transceiver for "
4354 "offer caps %" GST_PTR_FORMAT ", will only receive", offer_caps);
4358 GstCaps *trans_caps;
4359 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
4361 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
4362 kind = GST_WEBRTC_KIND_AUDIO;
4363 else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
4365 kind = GST_WEBRTC_KIND_VIDEO;
4367 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
4368 GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
4370 trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
4371 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4373 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
4374 " for mline %u with media kind %d", trans, i, kind);
4376 trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4378 gst_caps_unref (offer_caps);
4382 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4383 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4385 /* FIXME: technically this is a little overreaching as some fields we
4386 * we can deal with not having and/or we may have unrecognized fields
4387 * that we cannot actually support */
4389 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4390 gst_clear_caps (&trans_caps);
4392 answer_caps = gst_caps_ref (offer_caps);
4395 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4398 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
4400 if (gst_caps_is_empty (answer_caps)) {
4401 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
4402 gst_clear_caps (&answer_caps);
4403 gst_clear_caps (&offer_caps);
4407 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps)) {
4408 GstWebRTCKind caps_kind = webrtc_kind_from_caps (answer_caps);
4410 GST_WARNING_OBJECT (webrtc,
4411 "Trying to change kind of transceiver %" GST_PTR_FORMAT
4412 " at m-line %d from %s (%d) to %s (%d)", trans, rtp_trans->mline,
4413 gst_webrtc_kind_to_string (rtp_trans->kind), rtp_trans->kind,
4414 gst_webrtc_kind_to_string (caps_kind), caps_kind);
4417 answer_caps = gst_caps_make_writable (answer_caps);
4418 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
4419 GstStructure *s = gst_caps_get_structure (answer_caps, k);
4420 /* taken from the offer sdp already and already intersected above */
4421 gst_structure_remove_field (s, "a-mid");
4422 if (!trans->do_nack)
4423 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
4426 gst_sdp_media_set_media_from_caps (answer_caps, media);
4428 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
4431 original_target_pt = target_pt;
4433 _media_add_fec (media, trans, offer_caps, &target_pt);
4434 if (trans->do_nack) {
4435 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
4436 if (target_pt != original_target_pt)
4437 _media_add_rtx (media, trans, offer_caps, original_target_pt,
4441 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
4442 _media_add_ssrcs (media, answer_caps, webrtc,
4443 WEBRTC_TRANSCEIVER (rtp_trans));
4445 gst_caps_unref (answer_caps);
4448 /* set the new media direction */
4449 offer_dir = _get_direction_from_media (offer_media);
4450 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
4451 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4452 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
4453 "transceiver direction");
4454 gst_caps_unref (offer_caps);
4457 _media_replace_direction (media, answer_dir);
4459 if (!trans->stream) {
4460 TransportStream *item;
4463 _get_or_create_transport_stream (webrtc,
4464 bundled_mids ? bundle_idx : i, FALSE);
4465 webrtc_transceiver_set_transport (trans, item);
4469 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
4472 g_string_append_printf (bundled_mids, " %s", mid);
4475 /* set the a=fingerprint: for this transport */
4476 _add_fingerprint_to_media (trans->stream->transport, media);
4478 gst_caps_unref (offer_caps);
4480 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
4486 if (error && *error)
4487 GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
4489 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
4490 gst_sdp_media_free (media);
4491 gst_sdp_media_copy (offer_media, &media);
4492 gst_sdp_media_set_port_info (media, 0, 0);
4493 /* Clear error here as it is not propagated to the caller and the media
4494 * is just skipped, i.e. more iterations are going to happen. */
4495 g_clear_error (error);
4497 gst_sdp_message_add_media (ret, media);
4498 gst_sdp_media_free (media);
4502 gchar *mids = g_string_free (bundled_mids, FALSE);
4504 gst_sdp_message_add_attribute (ret, "group", mids);
4509 g_free (bundle_ufrag);
4512 g_free (bundle_pwd);
4514 /* FIXME: can we add not matched transceivers? */
4516 /* XXX: only true for the initial offerer */
4517 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
4520 g_strfreev (bundled);
4522 g_list_free (seen_transceivers);
4524 if (webrtc->priv->last_generated_offer)
4525 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4526 webrtc->priv->last_generated_offer = NULL;
4527 if (webrtc->priv->last_generated_answer)
4528 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4530 GstSDPMessage *copy;
4531 gst_sdp_message_copy (ret, ©);
4532 webrtc->priv->last_generated_answer =
4533 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
4541 GstStructure *options;
4542 GstWebRTCSDPType type;
4545 static GstStructure *
4546 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
4548 GstWebRTCSessionDescription *desc = NULL;
4549 GstSDPMessage *sdp = NULL;
4550 GstStructure *s = NULL;
4551 GError *error = NULL;
4553 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
4554 gst_webrtc_sdp_type_to_string (data->type), data->options);
4556 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
4557 sdp = _create_offer_task (webrtc, data->options, &error);
4558 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
4559 sdp = _create_answer_task (webrtc, data->options, &error);
4561 g_assert_not_reached ();
4566 desc = gst_webrtc_session_description_new (data->type, sdp);
4567 s = gst_structure_new ("application/x-gst-promise",
4568 gst_webrtc_sdp_type_to_string (data->type),
4569 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
4571 g_warn_if_fail (error != NULL);
4572 GST_WARNING_OBJECT (webrtc, "returning error: %s",
4573 error ? error->message : "Unknown");
4574 s = gst_structure_new ("application/x-gst-promise",
4575 "error", G_TYPE_ERROR, error, NULL);
4576 g_clear_error (&error);
4582 gst_webrtc_session_description_free (desc);
4588 _free_create_sdp_data (struct create_sdp *data)
4591 gst_structure_free (data->options);
4596 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
4597 const GstStructure * options, GstPromise * promise)
4599 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4602 data->options = gst_structure_copy (options);
4603 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
4605 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4606 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4608 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4609 "Could not create offer. webrtcbin is closed");
4610 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4611 "error", G_TYPE_ERROR, error, NULL);
4613 gst_promise_reply (promise, s);
4615 g_clear_error (&error);
4620 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
4621 const GstStructure * options, GstPromise * promise)
4623 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4626 data->options = gst_structure_copy (options);
4627 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
4629 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4630 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4632 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4633 "Could not create answer. webrtcbin is closed.");
4634 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4635 "error", G_TYPE_ERROR, error, NULL);
4637 gst_promise_reply (promise, s);
4639 g_clear_error (&error);
4643 static GstWebRTCBinPad *
4644 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
4645 GstWebRTCRTPTransceiver * trans, guint serial)
4647 GstWebRTCBinPad *pad;
4650 if (direction == GST_PAD_SINK) {
4651 if (serial == G_MAXUINT)
4652 serial = webrtc->priv->max_sink_pad_serial++;
4654 serial = webrtc->priv->src_pad_counter++;
4658 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
4660 pad = gst_webrtc_bin_pad_new (pad_name, direction);
4663 pad->trans = gst_object_ref (trans);
4668 static GstWebRTCRTPTransceiver *
4669 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
4670 const GstSDPMessage * sdp, guint media_idx)
4672 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4673 GstWebRTCRTPTransceiver *ret = NULL;
4676 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4677 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4679 if (g_strcmp0 (attr->key, "mid") == 0) {
4680 if ((ret = _find_transceiver_for_mid (webrtc, attr->value)))
4685 ret = _find_transceiver (webrtc, &media_idx,
4686 (FindTransceiverFunc) transceiver_match_for_mline);
4689 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
4694 _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
4696 GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4697 guint ulpfec_pt = 0, red_pt = 0;
4698 GstPad *sinkpad, *srcpad, *ghost;
4701 if (trans->stream) {
4703 transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
4704 red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
4707 if (trans->ulpfecenc || trans->redenc) {
4708 g_critical ("webrtcbin: duplicate call to create a fec encoder or "
4713 GST_DEBUG_OBJECT (webrtc,
4714 "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
4717 ret = gst_bin_new (NULL);
4719 trans->ulpfecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4720 gst_object_ref_sink (trans->ulpfecenc);
4721 if (!gst_bin_add (GST_BIN (ret), trans->ulpfecenc))
4722 g_warn_if_reached ();
4723 sinkpad = gst_element_get_static_pad (trans->ulpfecenc, "sink");
4725 g_object_bind_property (rtp_trans, "fec-percentage", trans->ulpfecenc,
4726 "percentage", G_BINDING_DEFAULT);
4728 trans->redenc = gst_element_factory_make ("rtpredenc", NULL);
4729 gst_object_ref_sink (trans->redenc);
4731 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
4732 rtp_trans->mline, red_pt);
4734 gst_bin_add (GST_BIN (ret), trans->redenc);
4735 gst_element_link (trans->ulpfecenc, trans->redenc);
4737 ghost = gst_ghost_pad_new ("sink", sinkpad);
4738 gst_clear_object (&sinkpad);
4739 gst_element_add_pad (ret, ghost);
4742 srcpad = gst_element_get_static_pad (trans->redenc, "src");
4743 ghost = gst_ghost_pad_new ("src", srcpad);
4744 gst_clear_object (&srcpad);
4745 gst_element_add_pad (ret, ghost);
4752 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
4754 GstStructure *s = user_data;
4756 gst_structure_id_set_value (s, field_id, value);
4761 #define GST_WEBRTC_PAYLOAD_TYPE "gst.webrtcbin.payload.type"
4764 try_match_transceiver_with_fec_decoder (GstWebRTCBin * webrtc,
4765 WebRTCTransceiver * trans)
4769 for (l = trans->stream->fecdecs; l; l = l->next) {
4770 GstElement *fecdec = GST_ELEMENT (l->data);
4771 gboolean found_transceiver = FALSE;
4776 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fecdec),
4777 GST_WEBRTC_PAYLOAD_TYPE));
4778 if (original_pt <= 0) {
4779 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
4780 "transceiver, fec decoder %" GST_PTR_FORMAT " does not contain a "
4781 "valid payload type", fecdec);
4785 for (i = 0; i < trans->stream->ptmap->len; i++) {
4786 PtMapItem *item = &g_array_index (trans->stream->ptmap, PtMapItem, i);
4788 /* FIXME: this only works for a 1-1 original_pt->fec_pt mapping */
4789 if (original_pt == item->pt && item->media_idx != -1
4790 && item->media_idx == trans->parent.mline) {
4791 if (trans->ulpfecdec) {
4792 GST_FIXME_OBJECT (trans, "cannot");
4793 gst_clear_object (&trans->ulpfecdec);
4795 trans->ulpfecdec = gst_object_ref (fecdec);
4796 found_transceiver = TRUE;
4801 if (!found_transceiver) {
4802 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
4809 _set_internal_rtpbin_element_props_from_stream (GstWebRTCBin * webrtc,
4810 TransportStream * stream)
4812 GstStructure *merged_local_rtx_ssrc_map;
4813 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4814 GValue red_pt_array = { 0, };
4819 gst_value_array_init (&red_pt_array, 0);
4821 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4822 GST_DEBUG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4824 for (i = 0; i < rtx_count; i++) {
4825 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4826 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4827 const gchar *apt = gst_structure_get_string (s, "apt");
4829 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4830 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4833 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4834 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4835 stream->rtxsend, pt_map);
4837 if (stream->rtxreceive)
4838 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4839 if (stream->rtxsend)
4840 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4842 gst_structure_free (pt_map);
4843 g_clear_pointer (&rtx_pt, g_free);
4845 merged_local_rtx_ssrc_map =
4846 gst_structure_new_empty ("application/x-rtp-ssrc-map");
4848 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4849 GstWebRTCRTPTransceiver *rtp_trans =
4850 g_ptr_array_index (webrtc->priv->transceivers, i);
4851 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4853 if (trans->stream == stream) {
4854 gint ulpfec_pt, red_pt = 0;
4856 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC", rtp_trans->mline);
4860 red_pt = transport_stream_get_pt (stream, "RED", rtp_trans->mline);
4864 GValue ptval = { 0, };
4866 g_value_init (&ptval, G_TYPE_INT);
4867 g_value_set_int (&ptval, red_pt);
4868 gst_value_array_append_value (&red_pt_array, &ptval);
4869 g_value_unset (&ptval);
4872 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " transceiever %"
4873 GST_PTR_FORMAT " has FEC payload %d and RED payload %d", stream,
4874 trans, ulpfec_pt, red_pt);
4876 if (trans->ulpfecenc) {
4877 guint ulpfecenc_pt = ulpfec_pt;
4879 if (ulpfecenc_pt == 0)
4882 g_object_set (trans->ulpfecenc, "pt", ulpfecenc_pt, "multipacket",
4883 rtp_trans->kind == GST_WEBRTC_KIND_VIDEO, "percentage",
4884 trans->fec_percentage, NULL);
4887 try_match_transceiver_with_fec_decoder (webrtc, trans);
4888 if (trans->ulpfecdec) {
4889 g_object_set (trans->ulpfecdec, "pt", ulpfec_pt, NULL);
4892 if (trans->redenc) {
4893 gboolean always_produce = TRUE;
4895 /* passthrough settings */
4897 always_produce = FALSE;
4899 g_object_set (trans->redenc, "pt", red_pt, "allow-no-red-blocks",
4900 always_produce, NULL);
4903 if (trans->local_rtx_ssrc_map) {
4904 gst_structure_foreach (trans->local_rtx_ssrc_map,
4905 _merge_structure, merged_local_rtx_ssrc_map);
4910 if (stream->rtxsend)
4911 g_object_set (stream->rtxsend, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
4912 gst_clear_structure (&merged_local_rtx_ssrc_map);
4914 if (stream->reddec) {
4915 g_object_set_property (G_OBJECT (stream->reddec), "payloads",
4919 g_value_unset (&red_pt_array);
4923 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
4928 * ,--------------------------------------------webrtcbin--------------------------------------------,
4930 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
4931 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
4932 * ; ,---clocksync---, ; ; ; ; ;
4933 * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
4934 * ; sink_%u ; ; ,---fec encoder---, ; ; '---------------------' ;
4935 * o---------o sink src o-o sink src o--o send_rtp_sink_%u ; ;
4936 * ; '---------------' ,-----------------, '--------------------' ;
4937 * '-------------------------------------------------------------------------------------------------'
4942 * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
4944 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
4945 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
4947 * ; sink_%u ,---clocksync---, ,---fec encoder---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
4948 * o----------o sink src o-o sink src o--o sink_%u ; ; ; '---------------------' ;
4949 * ; '---------------' ,-----------------, ; ; ; ; ;
4950 * ; ; src o-o send_rtp_sink_%u ; ;
4951 * ; sink_%u ,---clocksync---, ,---fec encoder---, ; ; ; ; ;
4952 * o----------o sink src o-o sink src o--o sink%u ; '--------------------' ;
4953 * ; '---------------' ,-----------------, '------------' ;
4954 * '-----------------------------------------------------------------------------------------------------------------'
4956 GstPadTemplate *rtp_templ;
4957 GstPad *rtp_sink, *sinkpad, *srcpad;
4959 WebRTCTransceiver *trans;
4960 GstElement *clocksync;
4961 GstElement *fec_encoder;
4963 g_return_val_if_fail (pad->trans != NULL, NULL);
4965 trans = WEBRTC_TRANSCEIVER (pad->trans);
4967 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
4969 g_assert (trans->stream);
4971 clocksync = gst_element_factory_make ("clocksync", NULL);
4972 g_object_set (clocksync, "sync", TRUE, NULL);
4973 gst_bin_add (GST_BIN (webrtc), clocksync);
4974 gst_element_sync_state_with_parent (clocksync);
4976 srcpad = gst_element_get_static_pad (clocksync, "src");
4978 fec_encoder = _build_fec_encoder (webrtc, trans);
4980 g_warn_if_reached ();
4984 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
4986 gst_bin_add (GST_BIN (webrtc), fec_encoder);
4987 gst_element_sync_state_with_parent (fec_encoder);
4989 sinkpad = gst_element_get_static_pad (fec_encoder, "sink");
4990 if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)
4991 g_warn_if_reached ();
4992 gst_clear_object (&srcpad);
4993 gst_clear_object (&sinkpad);
4994 sinkpad = gst_element_get_static_pad (clocksync, "sink");
4995 srcpad = gst_element_get_static_pad (fec_encoder, "src");
4997 if (!webrtc->rtpfunnel) {
4999 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
5000 "send_rtp_sink_%u");
5001 g_assert (rtp_templ);
5003 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
5005 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
5007 gst_pad_link (srcpad, rtp_sink);
5008 gst_object_unref (rtp_sink);
5010 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
5011 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5012 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
5013 g_warn_if_reached ();
5016 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
5017 GstPad *funnel_sinkpad =
5018 gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
5020 gst_pad_link (srcpad, funnel_sinkpad);
5023 gst_object_unref (funnel_sinkpad);
5026 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
5028 gst_clear_object (&srcpad);
5029 gst_clear_object (&sinkpad);
5031 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
5033 return GST_PAD (pad);
5036 /* output pads are receiving elements */
5038 _connect_output_stream (GstWebRTCBin * webrtc,
5039 TransportStream * stream, guint session_id)
5042 * ,------------------------webrtcbin------------------------,
5043 * ; ,---------rtpbin---------, ;
5044 * ; ,-transport_receive_%u--, ; ; ;
5045 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
5047 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
5048 * ; '-----------------------' ; ; ; src_%u
5049 * ; ; recv_rtp_src_%u_%u_%u o--o
5050 * ; '------------------------' ;
5051 * '---------------------------------------------------------'
5055 if (stream->output_connected) {
5056 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
5057 "connected to rtpbin. Not connecting", stream);
5061 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
5062 session_id, stream);
5064 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
5065 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
5066 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
5067 g_warn_if_reached ();
5070 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
5072 /* The webrtcbin src_%u output pads will be created when rtpbin receives
5073 * data on that stream in on_rtpbin_pad_added() */
5075 stream->output_connected = TRUE;
5085 _clear_ice_candidate_item (IceCandidateItem * item)
5087 g_free (item->candidate);
5091 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
5092 gboolean drop_invalid)
5094 GstWebRTCICEStream *stream;
5096 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
5097 if (stream == NULL) {
5099 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
5102 IceCandidateItem new;
5103 new.mlineindex = item->mlineindex;
5104 new.candidate = g_strdup (item->candidate);
5105 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
5108 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5109 ICE_UNLOCK (webrtc);
5114 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
5115 item->mlineindex, item->candidate);
5117 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
5121 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
5122 const GstSDPMedia * media)
5125 GstWebRTCICEStream *stream = NULL;
5127 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
5128 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
5129 if (g_strcmp0 (attr->key, "candidate") == 0) {
5133 stream = _find_ice_stream_for_session (webrtc, mlineindex);
5134 if (stream == NULL) {
5135 GST_DEBUG_OBJECT (webrtc,
5136 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
5140 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
5141 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
5142 mlineindex, candidate);
5143 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
5150 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
5151 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
5153 GstSDPMedia *media = NULL;
5155 if (mline_index < sdp->medias->len) {
5156 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
5159 if (media == NULL) {
5160 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
5164 // Add the candidate as an attribute, first stripping off the existing
5165 // candidate: key from the string description
5166 if (strlen (candidate) < 10) {
5167 GST_WARNING_OBJECT (webrtc,
5168 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
5172 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
5176 _filter_sdp_fields (GQuark field_id, const GValue * value,
5177 GstStructure * new_structure)
5179 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
5180 gst_structure_id_set_value (new_structure, field_id, value);
5186 transport_stream_ptmap_get_rtp_header_extension_id (TransportStream * stream,
5187 const char *rtphdrext_uri)
5191 for (i = 0; i < stream->ptmap->len; i++) {
5192 PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
5195 id = caps_get_rtp_header_extension_id (item->caps, rtphdrext_uri);
5204 ensure_rtx_hdr_ext (TransportStream * stream)
5206 stream->rtphdrext_id_stream_id =
5207 transport_stream_ptmap_get_rtp_header_extension_id (stream,
5208 RTPHDREXT_STREAM_ID);
5209 stream->rtphdrext_id_repaired_stream_id =
5210 transport_stream_ptmap_get_rtp_header_extension_id (stream,
5211 RTPHDREXT_REPAIRED_STREAM_ID);
5213 /* TODO: removing header extensions usage from rtx on renegotiation */
5215 if (stream->rtxsend) {
5216 if (stream->rtphdrext_id_stream_id != -1 && !stream->rtxsend_stream_id) {
5217 stream->rtxsend_stream_id =
5218 gst_rtp_header_extension_create_from_uri (RTPHDREXT_STREAM_ID);
5219 if (!stream->rtxsend_stream_id)
5220 g_warn_if_reached ();
5221 gst_rtp_header_extension_set_id (stream->rtxsend_stream_id,
5222 stream->rtphdrext_id_stream_id);
5224 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5225 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_stream_id,
5226 stream->rtphdrext_id_stream_id, stream->rtxsend);
5228 g_signal_emit_by_name (stream->rtxsend, "add-extension",
5229 stream->rtxsend_stream_id);
5232 if (stream->rtphdrext_id_repaired_stream_id != -1
5233 && !stream->rtxsend_repaired_stream_id) {
5234 stream->rtxsend_repaired_stream_id =
5235 gst_rtp_header_extension_create_from_uri
5236 (RTPHDREXT_REPAIRED_STREAM_ID);
5237 if (!stream->rtxsend_repaired_stream_id)
5238 g_warn_if_reached ();
5239 gst_rtp_header_extension_set_id (stream->rtxsend_repaired_stream_id,
5240 stream->rtphdrext_id_repaired_stream_id);
5242 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5243 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_repaired_stream_id,
5244 stream->rtphdrext_id_repaired_stream_id, stream->rtxsend);
5246 g_signal_emit_by_name (stream->rtxsend, "add-extension",
5247 stream->rtxsend_repaired_stream_id);
5251 if (stream->rtxreceive) {
5252 if (stream->rtphdrext_id_stream_id != -1 && !stream->rtxreceive_stream_id) {
5253 stream->rtxreceive_stream_id =
5254 gst_rtp_header_extension_create_from_uri (RTPHDREXT_STREAM_ID);
5255 if (!stream->rtxreceive_stream_id)
5256 g_warn_if_reached ();
5257 gst_rtp_header_extension_set_id (stream->rtxreceive_stream_id,
5258 stream->rtphdrext_id_stream_id);
5260 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5261 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_stream_id,
5262 stream->rtphdrext_id_stream_id, stream->rtxreceive);
5264 g_signal_emit_by_name (stream->rtxreceive, "add-extension",
5265 stream->rtxreceive_stream_id);
5268 if (stream->rtphdrext_id_repaired_stream_id != -1
5269 && !stream->rtxreceive_repaired_stream_id) {
5270 stream->rtxreceive_repaired_stream_id =
5271 gst_rtp_header_extension_create_from_uri
5272 (RTPHDREXT_REPAIRED_STREAM_ID);
5273 if (!stream->rtxreceive_repaired_stream_id)
5274 g_warn_if_reached ();
5275 gst_rtp_header_extension_set_id (stream->rtxreceive_repaired_stream_id,
5276 stream->rtphdrext_id_repaired_stream_id);
5278 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5279 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_repaired_stream_id,
5280 stream->rtphdrext_id_repaired_stream_id, stream->rtxreceive);
5282 g_signal_emit_by_name (stream->rtxreceive, "add-extension",
5283 stream->rtxreceive_repaired_stream_id);
5289 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
5290 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
5294 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5297 proto = gst_sdp_media_get_proto (media);
5298 if (proto != NULL) {
5299 /* Parse global SDP attributes once */
5300 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
5301 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
5302 gst_sdp_message_attributes_to_caps (sdp, global_caps);
5303 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
5304 gst_sdp_media_attributes_to_caps (media, global_caps);
5306 len = gst_sdp_media_formats_len (media);
5307 for (i = 0; i < len; i++) {
5308 GstCaps *caps, *outcaps;
5314 pt = atoi (gst_sdp_media_get_format (media, i));
5316 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
5319 caps = gst_sdp_media_get_caps_from_media (media, pt);
5321 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
5325 /* Merge in global caps */
5326 /* Intersect will merge in missing fields to the current caps */
5327 outcaps = gst_caps_intersect (caps, global_caps);
5328 gst_caps_unref (caps);
5330 s = gst_caps_get_structure (outcaps, 0);
5331 gst_structure_set_name (s, "application/x-rtp");
5332 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
5333 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
5335 item.caps = gst_caps_new_empty ();
5337 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
5338 GstStructure *s = gst_caps_get_structure (outcaps, j);
5339 GstStructure *filtered =
5340 gst_structure_new_empty (gst_structure_get_name (s));
5342 gst_structure_foreach (s,
5343 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
5344 gst_caps_append_structure (item.caps, filtered);
5348 item.media_idx = media_idx;
5349 gst_caps_unref (outcaps);
5351 g_array_append_val (stream->ptmap, item);
5354 gst_caps_unref (global_caps);
5359 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
5360 const GstSDPMessage * sdp, guint media_idx,
5361 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
5362 GStrv bundled, guint bundle_idx, GError ** error)
5364 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
5365 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
5366 GstWebRTCRTPTransceiverDirection new_dir;
5367 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5368 GstWebRTCDTLSSetup new_setup;
5369 gboolean new_rtcp_rsize;
5370 ReceiveState receive_state = RECEIVE_STATE_UNSET;
5373 rtp_trans->mline = media_idx;
5375 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
5376 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
5377 GST_FIXME_OBJECT (webrtc, "Updating video transceiver %" GST_PTR_FORMAT
5378 " to audio, which isn't fully supported.", rtp_trans);
5379 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
5382 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
5383 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
5384 GST_FIXME_OBJECT (webrtc, "Updating audio transceiver %" GST_PTR_FORMAT
5385 " to video, which isn't fully supported.", rtp_trans);
5386 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
5389 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
5390 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
5392 if (g_strcmp0 (attr->key, "mid") == 0) {
5393 g_free (rtp_trans->mid);
5394 rtp_trans->mid = g_strdup (attr->value);
5399 const GstSDPMedia *local_media, *remote_media;
5400 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
5401 GstWebRTCDTLSSetup local_setup, remote_setup;
5404 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5407 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5410 local_setup = _get_dtls_setup_from_media (local_media);
5411 remote_setup = _get_dtls_setup_from_media (remote_media);
5412 new_setup = _get_final_setup (local_setup, remote_setup);
5413 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5414 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5415 "Cannot intersect direction attributes for media %u", media_idx);
5419 local_dir = _get_direction_from_media (local_media);
5420 remote_dir = _get_direction_from_media (remote_media);
5421 new_dir = _get_final_direction (local_dir, remote_dir);
5422 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
5423 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5424 "Cannot intersect dtls setup attributes for media %u", media_idx);
5428 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
5429 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
5430 && prev_dir != new_dir) {
5431 g_set_error (error, GST_WEBRTC_ERROR,
5432 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5433 "transceiver direction changes are not implemented. Media %u",
5438 if (!bundled || bundle_idx == media_idx) {
5439 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
5440 && _media_has_attribute_key (remote_media, "rtcp-rsize");
5444 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
5445 media_idx, &session);
5447 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
5448 g_object_unref (session);
5454 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5456 /* Not a bundled stream means this entire transport is inactive,
5457 * so set the receive state to BLOCK below */
5458 stream->active = FALSE;
5459 receive_state = RECEIVE_STATE_BLOCK;
5462 /* If this transceiver is active for sending or receiving,
5463 * we still need receive at least RTCP, so need to unblock
5464 * the receive bin below. */
5465 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
5466 receive_state = RECEIVE_STATE_PASS;
5467 stream->active = TRUE;
5470 if (new_dir != prev_dir) {
5471 gchar *prev_dir_s, *new_dir_s;
5472 guint rtp_session_id = bundled ? bundle_idx : media_idx;
5475 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
5478 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
5481 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
5482 " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
5484 g_free (prev_dir_s);
5489 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5490 GstWebRTCBinPad *pad;
5492 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
5494 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
5496 GstPad *peer = gst_pad_get_peer (target);
5498 gst_pad_send_event (peer, gst_event_new_eos ());
5499 gst_object_unref (peer);
5501 gst_object_unref (target);
5503 gst_object_unref (pad);
5506 /* XXX: send eos event up the sink pad as well? */
5509 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
5510 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5511 GstWebRTCBinPad *pad =
5512 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
5515 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
5516 " for transceiver %" GST_PTR_FORMAT, pad, trans);
5517 gst_object_unref (pad);
5519 GST_DEBUG_OBJECT (webrtc,
5520 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
5521 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
5523 _connect_input_stream (webrtc, pad);
5524 _add_pad (webrtc, pad);
5527 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
5528 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5529 GstWebRTCBinPad *pad =
5530 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5532 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
5533 " for transceiver %" GST_PTR_FORMAT, pad, trans);
5534 gst_object_unref (pad);
5536 GST_DEBUG_OBJECT (webrtc,
5537 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
5538 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
5541 if (!trans->stream) {
5542 TransportStream *item;
5545 _get_or_create_transport_stream (webrtc, rtp_session_id, FALSE);
5546 webrtc_transceiver_set_transport (trans, item);
5549 _connect_output_stream (webrtc, trans->stream, rtp_session_id);
5550 /* delay adding the pad until rtpbin creates the recv output pad
5551 * to ghost to so queries/events travel through the pipeline correctly
5552 * as soon as the pad is added */
5553 _add_pad_to_list (webrtc, pad);
5558 rtp_trans->mline = media_idx;
5559 rtp_trans->current_direction = new_dir;
5562 if (!bundled || bundle_idx == media_idx) {
5563 if (stream->rtxsend || stream->rtxreceive) {
5564 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
5567 g_object_set (stream, "dtls-client",
5568 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5571 /* Must be after setting the "dtls-client" so that data is not pushed into
5572 * the dtlssrtp elements before the ssl direction has been set which will
5573 * throw SSL errors */
5574 if (receive_state != RECEIVE_STATE_UNSET)
5575 transport_receive_bin_set_receive_state (stream->receive_bin,
5579 /* must be called with the pc lock held */
5581 _generate_data_channel_id (GstWebRTCBin * webrtc)
5584 gint new_id = -1, max_channels = 0;
5586 if (webrtc->priv->sctp_transport) {
5587 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5590 if (max_channels <= 0) {
5591 max_channels = 65534;
5594 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
5597 /* TODO: a better search algorithm */
5599 WebRTCDataChannel *channel;
5603 if (new_id < 0 || new_id >= max_channels) {
5604 /* exhausted id space */
5605 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
5606 "data channel id (max %i)", max_channels);
5610 /* client must generate even ids, server must generate odd ids */
5611 if (new_id % 2 == !(!is_client))
5614 channel = _find_data_channel_for_id (webrtc, new_id);
5623 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
5624 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
5627 const GstSDPMedia *local_media, *remote_media;
5628 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
5629 TransportReceiveBin *receive;
5630 int local_port, remote_port;
5631 guint64 local_max_size, remote_max_size, max_size;
5635 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5638 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5641 local_setup = _get_dtls_setup_from_media (local_media);
5642 remote_setup = _get_dtls_setup_from_media (remote_media);
5643 new_setup = _get_final_setup (local_setup, remote_setup);
5644 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5645 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5646 "Cannot intersect dtls setup for media %u", media_idx);
5650 /* data channel is always rtcp-muxed to avoid generating ICE candidates
5652 g_object_set (stream, "dtls-client",
5653 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5655 local_port = _get_sctp_port_from_media (local_media);
5656 remote_port = _get_sctp_port_from_media (local_media);
5657 if (local_port == -1 || remote_port == -1) {
5658 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5659 "Could not find sctp port for media %u (local %i, remote %i)",
5660 media_idx, local_port, remote_port);
5664 if (0 == (local_max_size =
5665 _get_sctp_max_message_size_from_media (local_media)))
5666 local_max_size = G_MAXUINT64;
5667 if (0 == (remote_max_size =
5668 _get_sctp_max_message_size_from_media (remote_media)))
5669 remote_max_size = G_MAXUINT64;
5670 max_size = MIN (local_max_size, remote_max_size);
5672 webrtc->priv->sctp_transport->max_message_size = max_size;
5675 guint orig_local_port, orig_remote_port;
5677 /* XXX: sctpassociation warns if we are in the wrong state */
5678 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5679 &orig_local_port, NULL);
5681 if (orig_local_port != local_port)
5682 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5685 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5686 &orig_remote_port, NULL);
5687 if (orig_remote_port != remote_port)
5688 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5693 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
5694 WebRTCDataChannel *channel;
5696 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
5698 if (channel->parent.id == -1)
5699 channel->parent.id = _generate_data_channel_id (webrtc);
5700 if (channel->parent.id == -1)
5701 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5702 ("%s", "Failed to generate an identifier for a data channel"), NULL);
5704 if (webrtc->priv->sctp_transport->association_established
5705 && !channel->parent.negotiated && !channel->opened) {
5706 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
5707 webrtc_data_channel_start_negotiation (channel);
5712 stream->active = TRUE;
5714 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
5715 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
5719 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
5722 GstWebRTCKind kind = GPOINTER_TO_INT (data);
5726 if (p1->mline != -1)
5730 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
5737 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
5740 GstPad *queue_srcpad;
5742 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
5747 if (webrtc->rtpfunnel)
5750 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
5751 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
5752 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
5754 queue = gst_element_factory_make ("queue", NULL);
5755 gst_bin_add (GST_BIN (webrtc), queue);
5756 gst_element_sync_state_with_parent (queue);
5758 gst_element_link (webrtc->rtpfunnel, queue);
5760 queue_srcpad = gst_element_get_static_pad (queue, "src");
5762 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
5763 rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
5765 gst_pad_link (queue_srcpad, rtp_sink);
5766 gst_object_unref (queue_srcpad);
5767 gst_object_unref (rtp_sink);
5769 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
5770 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5771 GST_ELEMENT (stream->send_bin), "rtp_sink"))
5772 g_warn_if_reached ();
5780 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
5781 GstWebRTCSessionDescription * sdp, GError ** error)
5784 gboolean ret = FALSE;
5785 GStrv bundled = NULL;
5786 guint bundle_idx = 0;
5787 TransportStream *bundle_stream = NULL;
5789 /* FIXME: With some peers, it's possible we could have
5790 * multiple bundles to deal with, although I've never seen one yet */
5791 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5792 if (!_parse_bundle (sdp->sdp, &bundled, error))
5797 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
5798 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5799 "Bundle tag is %s but no media found matching", bundled[0]);
5803 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
5804 _message_media_is_datachannel (sdp->sdp, bundle_idx));
5805 /* Mark the bundle stream as inactive to start. It will be set to TRUE
5806 * by any bundled mline that is active, and at the end we set the
5807 * receivebin to BLOCK if all mlines were inactive. */
5808 bundle_stream->active = FALSE;
5810 g_array_set_size (bundle_stream->ptmap, 0);
5811 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5812 /* When bundling, we need to do this up front, or else RTX
5813 * parameters aren't set up properly for the bundled streams */
5814 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
5816 ensure_rtx_hdr_ext (bundle_stream);
5818 _connect_rtpfunnel (webrtc, bundle_idx);
5821 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5822 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5823 TransportStream *stream;
5824 GstWebRTCRTPTransceiver *trans;
5825 guint transport_idx;
5827 /* skip rejected media */
5828 if (gst_sdp_media_get_port (media) == 0)
5832 transport_idx = bundle_idx;
5836 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5838 stream = _get_or_create_transport_stream (webrtc, transport_idx,
5839 _message_media_is_datachannel (sdp->sdp, transport_idx));
5841 /* When bundling, these were all set up above, but when not
5842 * bundling we need to do it now */
5843 g_array_set_size (stream->ptmap, 0);
5844 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
5845 ensure_rtx_hdr_ext (stream);
5849 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
5851 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
5852 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5853 "State mismatch. Could not find local transceiver by mline %u", i);
5856 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
5857 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
5858 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
5860 /* No existing transceiver, find an unused one */
5862 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
5863 kind = GST_WEBRTC_KIND_AUDIO;
5864 else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
5865 kind = GST_WEBRTC_KIND_VIDEO;
5867 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
5868 GST_STR_NULL (gst_sdp_media_get_media (media)));
5870 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
5871 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
5874 /* Still no transceiver? Create one */
5875 /* XXX: default to the advertised direction in the sdp for new
5876 * transceivers. The spec doesn't actually say what happens here, only
5877 * that calls to setDirection will change the value. Nothing about
5878 * a default value when the transceiver is created internally */
5880 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
5881 _get_direction_from_media (media), i, kind, NULL);
5882 webrtc_transceiver_set_transport (t, stream);
5883 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
5886 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
5887 trans, bundled, bundle_idx, error);
5888 if (error && *error)
5890 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
5891 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
5893 if (error && *error)
5896 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
5901 if (bundle_stream && bundle_stream->active == FALSE) {
5902 /* No bundled mline marked the bundle as active, so block the receive bin, as
5903 * this bundle is completely inactive */
5904 GST_LOG_OBJECT (webrtc,
5905 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
5906 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
5907 RECEIVE_STATE_BLOCK);
5913 g_strfreev (bundled);
5919 transceivers_media_num_cmp (GstWebRTCBin * webrtc,
5920 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
5925 return gst_sdp_message_medias_len (new->sdp) -
5926 gst_sdp_message_medias_len (previous->sdp);
5931 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
5936 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5937 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5938 GstWebRTCRTPTransceiver *rtp_trans;
5939 WebRTCTransceiver *trans;
5941 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5942 /* only look for matching mid */
5943 if (rtp_trans == NULL)
5946 trans = WEBRTC_TRANSCEIVER (rtp_trans);
5948 /* We only validate the locked mlines for now */
5949 if (!trans->mline_locked)
5952 if (rtp_trans->mline != i) {
5953 g_set_error (error, GST_WEBRTC_ERROR,
5954 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5955 "m-line with mid %s is at position %d, but was locked to %d, "
5956 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
5960 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
5961 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
5962 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
5963 char *trans_kind = gst_webrtc_kind_to_string (rtp_trans->kind);
5964 g_set_error (error, GST_WEBRTC_ERROR,
5965 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5966 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
5967 "%s media", i, GST_OBJECT_NAME (rtp_trans), trans_kind,
5968 gst_sdp_media_get_media (media));
5969 g_free (trans_kind);
5973 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
5974 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
5975 char *trans_kind = gst_webrtc_kind_to_string (rtp_trans->kind);
5976 g_set_error (error, GST_WEBRTC_ERROR,
5977 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5978 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
5979 "%s media", i, GST_OBJECT_NAME (rtp_trans), trans_kind,
5980 gst_sdp_media_get_media (media));
5981 g_free (trans_kind);
5991 struct set_description
5994 GstWebRTCSessionDescription *sdp;
5997 static GstWebRTCSessionDescription *
5998 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
5999 GstWebRTCSDPType type)
6002 case GST_WEBRTC_SDP_TYPE_OFFER:
6003 case GST_WEBRTC_SDP_TYPE_PRANSWER:
6004 case GST_WEBRTC_SDP_TYPE_ANSWER:
6005 if (source == SDP_LOCAL) {
6006 return webrtc->current_local_description;
6008 return webrtc->current_remote_description;
6010 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
6013 /* other values mean memory corruption/uninitialized! */
6014 g_assert_not_reached ();
6021 static GstWebRTCSessionDescription *
6022 get_last_generated_description (GstWebRTCBin * webrtc, SDPSource source,
6023 GstWebRTCSDPType type)
6026 case GST_WEBRTC_SDP_TYPE_OFFER:
6027 if (source == SDP_REMOTE)
6028 return webrtc->priv->last_generated_answer;
6030 return webrtc->priv->last_generated_offer;
6032 case GST_WEBRTC_SDP_TYPE_PRANSWER:
6033 case GST_WEBRTC_SDP_TYPE_ANSWER:
6034 if (source == SDP_LOCAL)
6035 return webrtc->priv->last_generated_answer;
6037 return webrtc->priv->last_generated_offer;
6038 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
6041 /* other values mean memory corruption/uninitialized! */
6042 g_assert_not_reached ();
6050 /* http://w3c.github.io/webrtc-pc/#set-description */
6051 static GstStructure *
6052 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
6054 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
6055 gboolean signalling_state_changed = FALSE;
6056 GError *error = NULL;
6057 GStrv bundled = NULL;
6058 guint bundle_idx = 0;
6062 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6063 webrtc->signaling_state);
6065 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
6066 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
6067 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
6068 _sdp_source_to_string (sd->source), type_str, state);
6069 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
6075 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
6078 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
6079 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
6083 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
6084 g_set_error (&error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6085 "Bundle tag is %s but no matching media found", bundled[0]);
6090 if (transceivers_media_num_cmp (webrtc,
6091 get_previous_description (webrtc, sd->source, sd->sdp->type),
6093 g_set_error_literal (&error, GST_WEBRTC_ERROR,
6094 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6095 "m=lines removed from the SDP. Processing a completely new connection "
6096 "is not currently supported.");
6100 if ((sd->sdp->type == GST_WEBRTC_SDP_TYPE_PRANSWER ||
6101 sd->sdp->type == GST_WEBRTC_SDP_TYPE_ANSWER) &&
6102 transceivers_media_num_cmp (webrtc,
6103 get_last_generated_description (webrtc, sd->source, sd->sdp->type),
6105 g_set_error_literal (&error, GST_WEBRTC_ERROR,
6106 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6107 "Answer doesn't have the same number of m-lines as the offer.");
6111 if (!check_locked_mlines (webrtc, sd->sdp, &error))
6114 switch (sd->sdp->type) {
6115 case GST_WEBRTC_SDP_TYPE_OFFER:{
6116 if (sd->source == SDP_LOCAL) {
6117 if (webrtc->pending_local_description)
6118 gst_webrtc_session_description_free
6119 (webrtc->pending_local_description);
6120 webrtc->pending_local_description =
6121 gst_webrtc_session_description_copy (sd->sdp);
6122 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
6124 if (webrtc->pending_remote_description)
6125 gst_webrtc_session_description_free
6126 (webrtc->pending_remote_description);
6127 webrtc->pending_remote_description =
6128 gst_webrtc_session_description_copy (sd->sdp);
6129 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
6133 case GST_WEBRTC_SDP_TYPE_ANSWER:{
6134 if (sd->source == SDP_LOCAL) {
6135 if (webrtc->current_local_description)
6136 gst_webrtc_session_description_free
6137 (webrtc->current_local_description);
6138 webrtc->current_local_description =
6139 gst_webrtc_session_description_copy (sd->sdp);
6141 if (webrtc->current_remote_description)
6142 gst_webrtc_session_description_free
6143 (webrtc->current_remote_description);
6144 webrtc->current_remote_description = webrtc->pending_remote_description;
6145 webrtc->pending_remote_description = NULL;
6147 if (webrtc->current_remote_description)
6148 gst_webrtc_session_description_free
6149 (webrtc->current_remote_description);
6150 webrtc->current_remote_description =
6151 gst_webrtc_session_description_copy (sd->sdp);
6153 if (webrtc->current_local_description)
6154 gst_webrtc_session_description_free
6155 (webrtc->current_local_description);
6156 webrtc->current_local_description = webrtc->pending_local_description;
6157 webrtc->pending_local_description = NULL;
6160 if (webrtc->pending_local_description)
6161 gst_webrtc_session_description_free (webrtc->pending_local_description);
6162 webrtc->pending_local_description = NULL;
6164 if (webrtc->pending_remote_description)
6165 gst_webrtc_session_description_free
6166 (webrtc->pending_remote_description);
6167 webrtc->pending_remote_description = NULL;
6169 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
6172 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
6173 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
6174 if (sd->source == SDP_LOCAL) {
6175 if (webrtc->pending_local_description)
6176 gst_webrtc_session_description_free
6177 (webrtc->pending_local_description);
6178 webrtc->pending_local_description = NULL;
6180 if (webrtc->pending_remote_description)
6181 gst_webrtc_session_description_free
6182 (webrtc->pending_remote_description);
6183 webrtc->pending_remote_description = NULL;
6186 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
6189 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
6190 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
6191 if (sd->source == SDP_LOCAL) {
6192 if (webrtc->pending_local_description)
6193 gst_webrtc_session_description_free
6194 (webrtc->pending_local_description);
6195 webrtc->pending_local_description =
6196 gst_webrtc_session_description_copy (sd->sdp);
6198 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
6200 if (webrtc->pending_remote_description)
6201 gst_webrtc_session_description_free
6202 (webrtc->pending_remote_description);
6203 webrtc->pending_remote_description =
6204 gst_webrtc_session_description_copy (sd->sdp);
6206 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
6212 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
6214 * If the mid value of an RTCRtpTransceiver was set to a non-null value
6215 * by the RTCSessionDescription that is being rolled back, set the mid
6216 * value of that transceiver to null, as described by [JSEP]
6217 * (section 4.1.7.2.).
6218 * If an RTCRtpTransceiver was created by applying the
6219 * RTCSessionDescription that is being rolled back, and a track has not
6220 * been attached to it via addTrack, remove that transceiver from
6221 * connection's set of transceivers, as described by [JSEP]
6222 * (section 4.1.7.2.).
6223 * Restore the value of connection's [[ sctpTransport]] internal slot
6224 * to its value at the last stable signaling state.
6228 if (webrtc->signaling_state != new_signaling_state) {
6229 webrtc->signaling_state = new_signaling_state;
6230 signalling_state_changed = TRUE;
6234 gboolean ice_controller = FALSE;
6236 /* get the current value so we don't change ice controller from TRUE to
6237 * FALSE on renegotiation or once set to TRUE for the initial local offer */
6238 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
6240 /* we control ice negotiation if we send the initial offer */
6242 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
6243 && webrtc->current_remote_description == NULL;
6244 /* or, if the remote is an ice-lite peer */
6245 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
6246 && webrtc->current_remote_description
6247 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
6250 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
6251 ice_controller ? "true" : "false");
6252 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
6255 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6258 /* media modifications */
6259 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
6262 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
6263 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
6264 GstWebRTCRTPTransceiverDirection new_dir;
6266 const GstSDPMedia *media;
6268 if (!pad->received_caps) {
6269 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
6274 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
6275 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
6280 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
6281 /* skip rejected media */
6282 if (gst_sdp_media_get_port (media) == 0) {
6283 /* FIXME: arrange for an appropriate flow return */
6284 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
6285 "a more correct flow return.");
6291 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
6296 new_dir = pad->trans->direction;
6297 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
6298 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
6299 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
6300 "data at the moment. Not connecting input stream yet", pad->trans);
6305 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
6306 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
6307 pad->trans, pad->received_caps);
6308 _connect_input_stream (webrtc, pad);
6309 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
6313 gst_object_unref (old->data);
6314 webrtc->priv->pending_sink_transceivers =
6315 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
6319 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
6320 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
6322 TransportStream *item;
6323 guint rtp_session_id = bundled ? bundle_idx : i;
6326 _get_or_create_transport_stream (webrtc, rtp_session_id,
6327 _message_media_is_datachannel (sd->sdp->sdp, rtp_session_id));
6329 if (sd->source == SDP_REMOTE) {
6332 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
6333 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
6335 if (g_strcmp0 (attr->key, "ssrc") == 0) {
6336 GStrv split = g_strsplit (attr->value, " ", 0);
6339 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
6340 && g_str_has_prefix (split[1], "cname:")) {
6341 if (!find_mid_ssrc_for_ssrc (webrtc,
6342 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY,
6343 rtp_session_id, ssrc))
6344 transport_stream_add_ssrc_map_item (item,
6345 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, ssrc, i);
6352 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
6353 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6355 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
6356 item->stream, ufrag, pwd);
6359 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
6360 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6362 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
6363 item->stream, ufrag, pwd);
6369 if (sd->source == SDP_LOCAL) {
6370 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
6371 IceStreamItem *item =
6372 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
6374 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
6378 /* Add any pending trickle ICE candidates if we have both offer and answer */
6379 if (webrtc->current_local_description && webrtc->current_remote_description) {
6382 GstWebRTCSessionDescription *remote_sdp =
6383 webrtc->current_remote_description;
6385 /* Add any remote ICE candidates from the remote description to
6386 * support non-trickle peers first */
6387 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
6388 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
6389 _add_ice_candidates_from_sdp (webrtc, i, media);
6393 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
6394 IceCandidateItem *item =
6395 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
6396 IceCandidateItem, i);
6398 _add_ice_candidate (webrtc, item, TRUE);
6400 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
6401 ICE_UNLOCK (webrtc);
6405 * If connection's signaling state changed above, fire an event named
6406 * signalingstatechange at connection.
6408 if (signalling_state_changed) {
6409 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6410 webrtc->signaling_state);
6411 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6412 new_signaling_state);
6413 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
6416 g_object_notify (G_OBJECT (webrtc), "signaling-state");
6423 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6424 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
6426 /* If connection's signaling state is now stable, update the
6427 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
6428 * was true both before and after this update, queue a task to check
6429 * connection's [[needNegotiation]] slot and, if still true, fire a
6430 * simple event named negotiationneeded at connection.*/
6431 _update_need_negotiation (webrtc);
6432 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
6433 _check_need_negotiation_task (webrtc, NULL);
6438 g_strfreev (bundled);
6441 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6442 "error", G_TYPE_ERROR, error, NULL);
6443 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
6444 g_clear_error (&error);
6452 _free_set_description_data (struct set_description *sd)
6455 gst_webrtc_session_description_free (sd->sdp);
6460 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
6461 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
6463 struct set_description *sd;
6465 if (remote_sdp == NULL)
6467 if (remote_sdp->sdp == NULL)
6470 sd = g_new0 (struct set_description, 1);
6471 sd->source = SDP_REMOTE;
6472 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
6474 if (!gst_webrtc_bin_enqueue_task (webrtc,
6475 (GstWebRTCBinFunc) _set_description_task, sd,
6476 (GDestroyNotify) _free_set_description_data, promise)) {
6478 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6479 "Could not set remote description. webrtcbin is closed.");
6480 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6481 "error", G_TYPE_ERROR, error, NULL);
6483 gst_promise_reply (promise, s);
6485 g_clear_error (&error);
6492 gst_promise_reply (promise, NULL);
6493 g_return_if_reached ();
6498 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
6499 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
6501 struct set_description *sd;
6503 if (local_sdp == NULL)
6505 if (local_sdp->sdp == NULL)
6508 sd = g_new0 (struct set_description, 1);
6509 sd->source = SDP_LOCAL;
6510 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
6512 if (!gst_webrtc_bin_enqueue_task (webrtc,
6513 (GstWebRTCBinFunc) _set_description_task, sd,
6514 (GDestroyNotify) _free_set_description_data, promise)) {
6516 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6517 "Could not set local description. webrtcbin is closed");
6518 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6519 "error", G_TYPE_ERROR, error, NULL);
6521 gst_promise_reply (promise, s);
6523 g_clear_error (&error);
6530 gst_promise_reply (promise, NULL);
6531 g_return_if_reached ();
6535 static GstStructure *
6536 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
6538 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
6539 IceCandidateItem new;
6540 new.mlineindex = item->mlineindex;
6541 new.candidate = g_steal_pointer (&item->candidate);
6544 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
6545 ICE_UNLOCK (webrtc);
6547 _add_ice_candidate (webrtc, item, FALSE);
6554 _free_ice_candidate_item (IceCandidateItem * item)
6556 _clear_ice_candidate_item (item);
6561 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
6564 IceCandidateItem *item;
6566 item = g_new0 (IceCandidateItem, 1);
6567 item->mlineindex = mline;
6568 if (attr && attr[0] != 0) {
6569 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
6570 item->candidate = g_strdup (attr);
6571 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
6572 item->candidate = g_strdup_printf ("a=%s", attr);
6574 gst_webrtc_bin_enqueue_task (webrtc,
6575 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
6576 (GDestroyNotify) _free_ice_candidate_item, NULL);
6579 static GstStructure *
6580 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
6586 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
6587 ICE_UNLOCK (webrtc);
6588 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
6589 return NULL; /* Nothing to process */
6591 /* Take the array so we can process it all and free it later
6592 * without holding the lock
6593 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
6595 items = webrtc->priv->pending_local_ice_candidates;
6596 /* Replace with a new array */
6597 webrtc->priv->pending_local_ice_candidates =
6598 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
6599 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
6600 (GDestroyNotify) _clear_ice_candidate_item);
6601 ICE_UNLOCK (webrtc);
6603 for (i = 0; i < items->len; i++) {
6604 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
6605 const gchar *cand = item->candidate;
6607 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
6608 /* stripping away "a=" */
6612 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
6613 item->mlineindex, cand);
6615 /* First, merge this ice candidate into the appropriate mline
6616 * in the local-description SDP.
6617 * Second, emit the on-ice-candidate signal for the app.
6619 * FIXME: This ICE candidate should be stored somewhere with
6620 * the associated mid and also merged back into any subsequent
6621 * local descriptions on renegotiation */
6622 if (webrtc->current_local_description)
6623 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
6624 item->mlineindex, cand);
6625 if (webrtc->pending_local_description)
6626 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
6627 item->mlineindex, cand);
6630 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
6631 0, item->mlineindex, cand);
6635 g_array_free (items, TRUE);
6641 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
6642 gchar * candidate, GstWebRTCBin * webrtc)
6644 IceCandidateItem item;
6645 gboolean queue_task = FALSE;
6647 item.mlineindex = session_id;
6648 item.candidate = g_strdup (candidate);
6651 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
6653 /* Let the first pending candidate queue a task each time, which will
6654 * handle any that arrive between now and when the task runs */
6655 if (webrtc->priv->pending_local_ice_candidates->len == 1)
6657 ICE_UNLOCK (webrtc);
6660 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
6661 gst_webrtc_bin_enqueue_task (webrtc,
6662 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
6669 GstPromise *promise;
6673 _free_get_stats (struct get_stats *stats)
6676 gst_object_unref (stats->pad);
6678 gst_promise_unref (stats->promise);
6682 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
6683 static GstStructure *
6684 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
6686 /* Our selector is the pad,
6687 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
6690 return gst_webrtc_bin_create_stats (webrtc, stats->pad);
6694 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
6695 GstPromise * promise)
6697 struct get_stats *stats;
6699 g_return_if_fail (promise != NULL);
6700 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
6702 stats = g_new0 (struct get_stats, 1);
6703 stats->promise = gst_promise_ref (promise);
6704 /* FIXME: check that pad exists in element */
6706 stats->pad = gst_object_ref (pad);
6708 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
6709 stats, (GDestroyNotify) _free_get_stats, promise)) {
6711 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6712 "Could not retrieve statistics. webrtcbin is closed.");
6713 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6714 "error", G_TYPE_ERROR, error, NULL);
6716 gst_promise_reply (promise, s);
6718 g_clear_error (&error);
6722 static GstWebRTCRTPTransceiver *
6723 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
6724 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
6726 WebRTCTransceiver *trans;
6728 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
6734 _create_webrtc_transceiver (webrtc, direction, -1,
6735 webrtc_kind_from_caps (caps), caps);
6736 GST_LOG_OBJECT (webrtc,
6737 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
6741 return gst_object_ref (trans);
6745 _deref_and_unref (GstObject ** object)
6747 gst_clear_object (object);
6751 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
6753 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
6758 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
6760 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6761 GstWebRTCRTPTransceiver *trans =
6762 g_ptr_array_index (webrtc->priv->transceivers, i);
6763 gst_object_ref (trans);
6764 g_array_append_val (arr, trans);
6771 static GstWebRTCRTPTransceiver *
6772 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
6774 GstWebRTCRTPTransceiver *trans = NULL;
6778 if (idx >= webrtc->priv->transceivers->len) {
6779 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
6783 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
6784 gst_object_ref (trans);
6792 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
6796 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
6797 g_return_val_if_fail (uri != NULL, FALSE);
6799 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
6802 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
6809 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
6811 GstPad *gpad = GST_PAD_CAST (user_data);
6813 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
6814 gst_pad_store_sticky_event (gpad, *event);
6819 static WebRTCDataChannel *
6820 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
6821 GstStructure * init_params)
6824 gint max_packet_lifetime;
6825 gint max_retransmits;
6826 const gchar *protocol;
6827 gboolean negotiated;
6829 GstWebRTCPriorityType priority;
6830 WebRTCDataChannel *ret;
6831 gint max_channels = 65534;
6833 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
6834 g_return_val_if_fail (label != NULL, NULL);
6835 g_return_val_if_fail (strlen (label) <= 65535, NULL);
6836 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
6839 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
6842 || !gst_structure_get_int (init_params, "max-packet-lifetime",
6843 &max_packet_lifetime))
6844 max_packet_lifetime = -1;
6846 || !gst_structure_get_int (init_params, "max-retransmits",
6848 max_retransmits = -1;
6849 /* both retransmits and lifetime cannot be set */
6850 g_return_val_if_fail ((max_packet_lifetime == -1)
6851 || (max_retransmits == -1), NULL);
6854 || !(protocol = gst_structure_get_string (init_params, "protocol")))
6856 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
6859 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
6861 if (!negotiated || !init_params
6862 || !gst_structure_get_int (init_params, "id", &id))
6865 g_return_val_if_fail (id != -1, NULL);
6866 g_return_val_if_fail (id < 65535, NULL);
6869 || !gst_structure_get_enum (init_params, "priority",
6870 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
6871 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
6873 /* FIXME: clamp max-retransmits and max-packet-lifetime */
6875 if (webrtc->priv->sctp_transport) {
6876 /* Let transport be the connection's [[SctpTransport]] slot.
6878 * If the [[DataChannelId]] slot is not null, transport is in
6879 * connected state and [[DataChannelId]] is greater or equal to the
6880 * transport's [[MaxChannels]] slot, throw an OperationError.
6882 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
6885 g_return_val_if_fail (id <= max_channels, NULL);
6888 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
6889 !_have_sctp_elements (webrtc))
6894 /* check if the id has been used already */
6896 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
6898 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
6899 ("Attempting to add a data channel with a duplicate ID: %i", id),
6905 } else if (webrtc->current_local_description
6906 && webrtc->current_remote_description && webrtc->priv->sctp_transport
6907 && webrtc->priv->sctp_transport->transport) {
6908 /* else we can only generate an id if we're configured already. The other
6909 * case for generating an id is on sdp setting */
6910 id = _generate_data_channel_id (webrtc);
6912 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
6913 ("%s", "Failed to generate an identifier for a data channel"), NULL);
6920 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
6921 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
6922 "max-retransmits", max_retransmits, "protocol", protocol,
6923 "negotiated", negotiated, "id", id, "priority", priority, NULL);
6931 g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL], 0,
6934 gst_bin_add (GST_BIN (webrtc), ret->appsrc);
6935 gst_bin_add (GST_BIN (webrtc), ret->appsink);
6937 gst_element_sync_state_with_parent (ret->appsrc);
6938 gst_element_sync_state_with_parent (ret->appsink);
6940 ret = gst_object_ref (ret);
6941 ret->webrtcbin = webrtc;
6942 g_ptr_array_add (webrtc->priv->data_channels, ret);
6945 gst_webrtc_bin_update_sctp_priority (webrtc);
6946 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
6947 if (webrtc->priv->sctp_transport &&
6948 webrtc->priv->sctp_transport->association_established
6949 && !ret->parent.negotiated) {
6950 webrtc_data_channel_start_negotiation (ret);
6952 _update_need_negotiation (webrtc);
6959 /* === rtpbin signal implementations === */
6962 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
6963 GstWebRTCBin * webrtc)
6965 gchar *new_pad_name = NULL;
6967 new_pad_name = gst_pad_get_name (new_pad);
6968 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
6969 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
6970 guint32 session_id = 0, ssrc = 0, pt = 0;
6971 SsrcMapItem *mid_entry;
6972 GstWebRTCRTPTransceiver *rtp_trans = NULL;
6973 WebRTCTransceiver *trans;
6974 TransportStream *stream;
6975 GstWebRTCBinPad *pad;
6978 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
6980 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
6984 media_idx = session_id;
6987 stream = _find_transport_for_session (webrtc, session_id);
6989 g_warn_if_reached ();
6992 find_mid_ssrc_for_ssrc (webrtc,
6993 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc);
6996 if (mid_entry->mid) {
6997 /* Can't use the mid_entry if the mid doesn't exist */
6998 rtp_trans = _find_transceiver_for_mid (webrtc, mid_entry->mid);
7000 g_assert_cmpint (rtp_trans->mline, ==, mid_entry->media_idx);
7004 if (mid_entry->media_idx != -1)
7005 media_idx = mid_entry->media_idx;
7007 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
7008 /* TODO: connect up to fakesink and reconnect later when this information
7009 * is known from RTCP SDES or RTP Header extension
7014 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
7016 g_warn_if_reached ();
7017 trans = WEBRTC_TRANSCEIVER (rtp_trans);
7018 g_assert (trans->stream == stream);
7020 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
7021 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
7022 " for rtpbin pad name %s", pad, new_pad_name);
7023 if (!_remove_pending_pad (webrtc, pad)) {
7024 /* assumption here is that rtpbin doesn't duplicate pads and that if
7025 * there is no pending pad, this is a duplicate stream for e.g. simulcast
7027 gst_clear_object (&pad);
7029 _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT);
7030 GST_TRACE_OBJECT (webrtc,
7031 "duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %"
7032 GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name);
7033 gst_object_ref_sink (pad);
7037 g_warn_if_reached ();
7038 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
7040 if (webrtc->priv->running)
7041 gst_pad_set_active (GST_PAD (pad), TRUE);
7045 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
7046 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
7048 gst_object_unref (pad);
7050 g_free (new_pad_name);
7053 /* only used for the receiving streams */
7055 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
7056 GstWebRTCBin * webrtc)
7058 TransportStream *stream;
7061 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
7065 stream = _find_transport_for_session (webrtc, session_id);
7067 goto unknown_session;
7069 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
7072 GST_DEBUG_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
7073 "session %d", ret, pt, session_id);
7081 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
7087 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
7088 GstWebRTCBin * webrtc)
7090 TransportStream *stream;
7091 GstElement *ret, *rtx;
7095 stream = _find_transport_for_session (webrtc, session_id);
7097 /* a rtp session without a stream is a webrtcbin bug */
7098 g_warn_if_reached ();
7102 if (stream->rtxsend) {
7103 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
7104 g_warn_if_reached ();
7108 GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
7109 "stream %" GST_PTR_FORMAT, session_id, stream);
7111 ret = gst_bin_new (NULL);
7112 rtx = gst_element_factory_make ("rtprtxsend", NULL);
7113 /* XXX: allow control from outside? */
7114 g_object_set (rtx, "max-size-packets", 500, NULL);
7116 if (!gst_bin_add (GST_BIN (ret), rtx))
7117 g_warn_if_reached ();
7118 ensure_rtx_hdr_ext (stream);
7120 stream->rtxsend = gst_object_ref (rtx);
7121 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7123 name = g_strdup_printf ("src_%u", session_id);
7124 pad = gst_element_get_static_pad (rtx, "src");
7125 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7126 g_warn_if_reached ();
7127 gst_clear_object (&pad);
7128 g_clear_pointer (&name, g_free);
7130 name = g_strdup_printf ("sink_%u", session_id);
7131 pad = gst_element_get_static_pad (rtx, "sink");
7132 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7133 g_warn_if_reached ();
7134 gst_clear_object (&pad);
7135 g_clear_pointer (&name, g_free);
7141 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
7142 GstWebRTCBin * webrtc)
7144 TransportStream *stream;
7145 GstPad *pad, *ghost;
7149 stream = _find_transport_for_session (webrtc, session_id);
7151 /* no transport stream before the session has been created is a webrtcbin
7152 * programming error! */
7153 g_warn_if_reached ();
7157 if (stream->rtxreceive) {
7158 GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
7159 g_warn_if_reached ();
7163 if (stream->reddec) {
7164 GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
7165 g_warn_if_reached ();
7169 GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
7170 "stream %" GST_PTR_FORMAT, session_id, stream);
7172 ret = gst_bin_new (NULL);
7174 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
7175 gst_object_ref (stream->rtxreceive);
7176 if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
7177 g_warn_if_reached ();
7179 ensure_rtx_hdr_ext (stream);
7181 stream->reddec = gst_element_factory_make ("rtpreddec", NULL);
7182 gst_object_ref (stream->reddec);
7183 if (!gst_bin_add (GST_BIN (ret), stream->reddec))
7184 g_warn_if_reached ();
7186 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7188 if (!gst_element_link (stream->rtxreceive, stream->reddec))
7189 g_warn_if_reached ();
7191 name = g_strdup_printf ("sink_%u", session_id);
7192 pad = gst_element_get_static_pad (stream->rtxreceive, "sink");
7193 ghost = gst_ghost_pad_new (name, pad);
7194 g_clear_pointer (&name, g_free);
7195 gst_clear_object (&pad);
7196 if (!gst_element_add_pad (ret, ghost))
7197 g_warn_if_reached ();
7199 name = g_strdup_printf ("src_%u", session_id);
7200 pad = gst_element_get_static_pad (stream->reddec, "src");
7201 ghost = gst_ghost_pad_new (name, pad);
7202 g_clear_pointer (&name, g_free);
7203 gst_clear_object (&pad);
7204 if (!gst_element_add_pad (ret, ghost))
7205 g_warn_if_reached ();
7211 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
7212 guint ssrc, guint pt, GstWebRTCBin * webrtc)
7214 TransportStream *stream;
7215 GstElement *ret = NULL;
7216 GObject *internal_storage;
7218 stream = _find_transport_for_session (webrtc, session_id);
7220 /* a rtp session without a stream is a webrtcbin bug */
7221 g_warn_if_reached ();
7225 /* TODO: for now, we only support ulpfec, but once we support
7226 * more algorithms, if the remote may use more than one algorithm,
7227 * we will want to do the following:
7229 * + Return a bin here, with the relevant FEC decoders plugged in
7230 * and their payload type set to 0
7232 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
7233 "stream %" GST_PTR_FORMAT, pt, session_id, stream);
7235 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
7237 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
7240 g_object_set (ret, "storage", internal_storage, NULL);
7241 g_clear_object (&internal_storage);
7243 g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
7244 GINT_TO_POINTER (pt));
7247 stream->fecdecs = g_list_prepend (stream->fecdecs, gst_object_ref (ret));
7248 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7255 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7256 GstWebRTCBin * webrtc)
7258 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
7261 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7266 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7267 GstWebRTCBin * webrtc)
7269 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
7272 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7277 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7278 GstWebRTCBin * webrtc)
7280 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
7284 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7289 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7290 GstWebRTCBin * webrtc)
7292 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
7298 find_or_add_ssrc_map_item (webrtc,
7299 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc, -1);
7304 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7305 GstWebRTCBin * webrtc)
7307 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
7311 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
7312 GstWebRTCBin * webrtc)
7314 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
7318 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
7319 GstWebRTCBin * webrtc)
7323 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
7325 g_signal_emit_by_name (rtpbin, "get-internal-session", session_id, &session);
7329 g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7333 g_object_get (source, "sdes", &sdes, NULL);
7335 /* TODO: when the sdes contains the mid, use that to correlate streams
7337 GST_DEBUG_OBJECT (webrtc, "session %u ssrc %u sdes %" GST_PTR_FORMAT,
7338 session_id, ssrc, sdes);
7340 gst_clear_structure (&sdes);
7341 gst_clear_object (&source);
7343 g_clear_object (&session);
7348 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
7349 GstWebRTCBin * webrtc)
7351 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
7355 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7356 GstWebRTCBin * webrtc)
7358 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
7361 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7366 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7367 GstWebRTCBin * webrtc)
7371 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
7375 mid = find_mid_ssrc_for_ssrc (webrtc,
7376 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, session_id, ssrc);
7378 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
7379 transport_stream_add_ssrc_map_item (stream,
7380 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, ssrc, -1);
7381 } else if (mid->mid) {
7382 /* XXX: when peers support the sdes rtcp item, use this to send the mid rtcp
7383 * sdes item. Requires being able to set the sdes on the rtpsource. */
7387 g_signal_emit_by_name (rtpbin, "get-internal-session", session_id,
7392 g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7395 const char *sdes_field_name;
7397 g_object_get (source, "sdes", &sdes, NULL);
7398 GST_WARNING_OBJECT (webrtc, "session %u ssrc %u retrieve sdes %"
7399 GST_PTR_FORMAT, session_id, ssrc, sdes);
7400 sdes_field_name = gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_MID);
7401 g_assert (sdes_field_name);
7402 gst_structure_set (sdes, sdes_field_name, G_TYPE_STRING, mid->mid,
7406 gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_RTP_STREAM_ID);
7407 g_assert (sdes_field_name);
7408 gst_structure_set (sdes, sdes_field_name, mid->rid, NULL);
7409 // TODO: repaired-rtp-stream-id
7411 // TODO: writable sdes?
7412 g_object_set (source, "sdes", sdes, NULL);
7413 GST_INFO_OBJECT (webrtc,
7414 "session %u ssrc %u set sdes %" GST_PTR_FORMAT, session_id, ssrc,
7417 gst_clear_structure (&sdes);
7418 gst_clear_object (&source);
7420 g_clear_object (&session);
7428 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7429 GstWebRTCBin * webrtc)
7431 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
7437 GstWebRTCBin *webrtc;
7438 GstElement *jitterbuffer;
7439 TransportStream *stream;
7444 jitter_buffer_set_retransmission (SsrcMapItem * item,
7445 const struct new_jb_args *data)
7447 GstWebRTCRTPTransceiver *trans;
7450 if (item->media_idx == -1)
7453 trans = _find_transceiver_for_mline (data->webrtc, item->media_idx);
7455 g_warn_if_reached ();
7459 do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
7460 /* We don't set do-retransmission on rtpbin as we want per-session control */
7461 GST_LOG_OBJECT (data->webrtc, "setting do-nack=%s for transceiver %"
7462 GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
7463 " rtp session %u ssrc %u", do_nack ? "true" : "false", trans,
7464 data->stream, data->stream->session_id, data->ssrc);
7465 g_object_set (data->jitterbuffer, "do-retransmission", do_nack, NULL);
7467 g_weak_ref_set (&item->rtpjitterbuffer, data->jitterbuffer);
7473 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
7474 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
7476 TransportStream *stream;
7477 struct new_jb_args d = { 0, };
7480 GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
7481 "session %u ssrc %u", jitterbuffer, session_id, ssrc);
7483 if (!(stream = _find_transport_for_session (webrtc, session_id))) {
7484 g_warn_if_reached ();
7489 d.jitterbuffer = jitterbuffer;
7492 transport_stream_filter_ssrc_map_item (stream, &d,
7493 (FindSsrcMapFunc) jitter_buffer_set_retransmission);
7500 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
7501 guint session_id, GstWebRTCBin * webrtc)
7503 guint64 latency = webrtc->priv->jb_latency;
7505 /* Add an extra 50 ms for safey */
7506 latency += RTPSTORAGE_EXTRA_TIME;
7507 latency *= GST_MSECOND;
7509 g_object_set (storage, "size-time", latency, NULL);
7513 _create_rtpbin (GstWebRTCBin * webrtc)
7517 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
7520 /* mandated by WebRTC */
7521 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
7523 g_object_set (rtpbin, "do-lost", TRUE, NULL);
7525 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
7527 g_signal_connect (rtpbin, "request-pt-map",
7528 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
7529 g_signal_connect (rtpbin, "request-aux-sender",
7530 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
7531 g_signal_connect (rtpbin, "request-aux-receiver",
7532 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
7533 g_signal_connect (rtpbin, "new-storage",
7534 G_CALLBACK (on_rtpbin_new_storage), webrtc);
7535 g_signal_connect (rtpbin, "request-fec-decoder-full",
7536 G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
7537 g_signal_connect (rtpbin, "on-bye-ssrc",
7538 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
7539 g_signal_connect (rtpbin, "on-bye-timeout",
7540 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
7541 g_signal_connect (rtpbin, "on-new-ssrc",
7542 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
7543 g_signal_connect (rtpbin, "on-new-sender-ssrc",
7544 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
7545 g_signal_connect (rtpbin, "on-sender-ssrc-active",
7546 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
7547 g_signal_connect (rtpbin, "on-sender-timeout",
7548 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
7549 g_signal_connect (rtpbin, "on-ssrc-active",
7550 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
7551 g_signal_connect (rtpbin, "on-ssrc-collision",
7552 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
7553 g_signal_connect (rtpbin, "on-ssrc-sdes",
7554 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
7555 g_signal_connect (rtpbin, "on-ssrc-validated",
7556 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
7557 g_signal_connect (rtpbin, "on-timeout",
7558 G_CALLBACK (on_rtpbin_timeout), webrtc);
7559 g_signal_connect (rtpbin, "new-jitterbuffer",
7560 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
7565 static GstStateChangeReturn
7566 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
7568 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7569 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
7571 GST_DEBUG ("changing state: %s => %s",
7572 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
7573 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
7575 switch (transition) {
7576 case GST_STATE_CHANGE_NULL_TO_READY:{
7577 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
7578 return GST_STATE_CHANGE_FAILURE;
7579 _start_thread (webrtc);
7581 _update_need_negotiation (webrtc);
7585 case GST_STATE_CHANGE_READY_TO_PAUSED:
7586 webrtc->priv->running = TRUE;
7592 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
7593 if (ret == GST_STATE_CHANGE_FAILURE)
7596 switch (transition) {
7597 case GST_STATE_CHANGE_READY_TO_PAUSED:
7598 /* Mangle the return value to NO_PREROLL as that's what really is
7599 * occurring here however cannot be propagated correctly due to nicesrc
7600 * requiring that it be in PLAYING already in order to send/receive
7602 ret = GST_STATE_CHANGE_NO_PREROLL;
7604 case GST_STATE_CHANGE_PAUSED_TO_READY:
7605 webrtc->priv->running = FALSE;
7607 case GST_STATE_CHANGE_READY_TO_NULL:
7608 _stop_thread (webrtc);
7617 static GstPadProbeReturn
7618 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
7620 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
7622 return GST_PAD_PROBE_OK;
7626 peek_sink_buffer (GstWebRTCBin * webrtc, guint rtp_session_id,
7627 guint media_idx, WebRTCTransceiver * trans, GstBuffer * buffer)
7629 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
7633 if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp))
7635 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
7636 gst_rtp_buffer_unmap (&rtp);
7639 GST_WARNING_OBJECT (webrtc,
7640 "incoming buffer does not contain a valid ssrc");
7646 find_or_add_ssrc_map_item (webrtc,
7647 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session_id, ssrc,
7649 if (item->media_idx == -1) {
7652 GST_DEBUG_OBJECT (webrtc, "updating media idx of ssrc item %p to %u", item,
7654 item->media_idx = media_idx;
7656 /* ensure that the rtx mapping contains a valid ssrc to use for rtx when
7657 * used even when there are no ssrc's in the input/codec preferences caps */
7658 str = g_strdup_printf ("%u", ssrc);
7659 if (!gst_structure_has_field_typed (trans->local_rtx_ssrc_map, str,
7661 /* TODO: ssrc-collision? */
7662 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
7663 g_random_int (), NULL);
7664 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
7671 static GstPadProbeReturn
7672 sink_pad_buffer_peek (GstPad * pad, GstPadProbeInfo * info,
7673 GstWebRTCBin * webrtc)
7675 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7676 WebRTCTransceiver *trans;
7677 guint rtp_session_id, media_idx;
7679 if (!webrtc_pad->trans)
7680 return GST_PAD_PROBE_OK;
7682 trans = (WebRTCTransceiver *) webrtc_pad->trans;
7684 return GST_PAD_PROBE_OK;
7686 rtp_session_id = trans->stream->session_id;
7687 media_idx = webrtc_pad->trans->mline;
7689 if (media_idx != G_MAXUINT)
7690 return GST_PAD_PROBE_OK;
7692 if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
7693 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
7694 peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
7695 } else if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
7696 GstBufferList *list = GST_PAD_PROBE_INFO_BUFFER_LIST (info);
7699 n = gst_buffer_list_length (list);
7700 for (i = 0; i < n; i++) {
7701 GstBuffer *buffer = gst_buffer_list_get (list, i);
7702 peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
7705 g_assert_not_reached ();
7708 return GST_PAD_PROBE_OK;
7712 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
7713 const gchar * name, const GstCaps * caps)
7715 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7716 GstWebRTCRTPTransceiver *trans = NULL;
7717 GstWebRTCBinPad *pad = NULL;
7719 gboolean lock_mline = FALSE;
7721 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
7724 if (templ->direction != GST_PAD_SINK ||
7725 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
7726 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
7732 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
7733 /* no name given when requesting the pad, use next available int */
7734 serial = webrtc->priv->max_sink_pad_serial++;
7736 /* parse serial number from requested padname */
7737 serial = g_ascii_strtoull (&name[5], NULL, 10);
7742 GstWebRTCBinPad *pad2;
7744 trans = _find_transceiver_for_mline (webrtc, serial);
7747 /* Reject transceivers that are only for receiving ... */
7748 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
7749 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
7751 g_enum_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
7753 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7754 " existing m-line %d, but the transceiver's direction is %s",
7755 name, serial, direction);
7760 /* Reject transceivers that already have a pad allocated */
7761 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
7763 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
7764 " but the transceiver associated with this m-line already has pad"
7765 " %s", name, serial, GST_PAD_NAME (pad2));
7766 gst_object_unref (pad2);
7771 GST_OBJECT_LOCK (trans);
7772 if (trans->codec_preferences &&
7773 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
7774 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7775 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
7776 " don't match existing codec preferences %" GST_PTR_FORMAT,
7777 name, serial, caps, trans->codec_preferences);
7778 GST_OBJECT_UNLOCK (trans);
7781 GST_OBJECT_UNLOCK (trans);
7783 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
7784 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
7786 if (trans->kind != kind) {
7787 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7788 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
7789 " don't match transceiver kind %d",
7790 name, serial, caps, trans->kind);
7798 /* Let's try to find a free transceiver that matches */
7800 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
7803 kind = webrtc_kind_from_caps (caps);
7805 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
7806 GstWebRTCRTPTransceiver *tmptrans =
7807 g_ptr_array_index (webrtc->priv->transceivers, i);
7808 GstWebRTCBinPad *pad2;
7809 gboolean has_matching_caps;
7811 /* Ignore transceivers with a non-matching kind */
7812 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
7813 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
7816 /* Ignore stopped transmitters */
7817 if (tmptrans->stopped)
7820 /* Ignore transceivers that are only for receiving ... */
7821 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
7822 || tmptrans->direction ==
7823 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
7826 /* Ignore transceivers that already have a pad allocated */
7827 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
7829 gst_object_unref (pad2);
7833 GST_OBJECT_LOCK (tmptrans);
7834 has_matching_caps = (caps && tmptrans->codec_preferences &&
7835 !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
7836 GST_OBJECT_UNLOCK (tmptrans);
7837 /* Ignore transceivers with non-matching caps */
7838 if (!has_matching_caps)
7847 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
7848 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
7849 webrtc_kind_from_caps (caps), NULL));
7850 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
7852 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
7853 " for mline %u", trans, serial);
7855 if (!_update_transceiver_kind_from_caps (trans, caps)) {
7856 GstWebRTCKind caps_kind = webrtc_kind_from_caps (caps);
7858 GST_WARNING_OBJECT (webrtc,
7859 "Trying to change kind of transceiver %" GST_PTR_FORMAT
7860 " at m-line %d from %s (%d) to %s (%d)", trans, serial,
7861 gst_webrtc_kind_to_string (trans->kind), trans->kind,
7862 gst_webrtc_kind_to_string (caps_kind), caps_kind);
7866 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
7868 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
7869 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
7870 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
7871 webrtc->priv->pending_sink_transceivers =
7872 g_list_append (webrtc->priv->pending_sink_transceivers,
7873 gst_object_ref (pad));
7875 gst_pad_add_probe (GST_PAD (pad),
7876 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
7877 (GstPadProbeCallback) sink_pad_buffer_peek, webrtc, NULL);
7880 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
7881 wtrans->mline_locked = TRUE;
7882 trans->mline = serial;
7887 _add_pad (webrtc, pad);
7889 return GST_PAD (pad);
7897 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
7899 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7900 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7902 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
7904 /* remove the transceiver from the pad so that subsequent code doesn't use
7905 * a possibly dead transceiver */
7907 if (webrtc_pad->trans)
7908 gst_object_unref (webrtc_pad->trans);
7909 webrtc_pad->trans = NULL;
7910 gst_caps_replace (&webrtc_pad->received_caps, NULL);
7913 _remove_pad (webrtc, webrtc_pad);
7916 _update_need_negotiation (webrtc);
7921 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
7926 /* Add an extra 50 ms for safety */
7927 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
7928 latency_ns *= GST_MSECOND;
7930 for (i = 0; i < webrtc->priv->transports->len; i++) {
7931 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
7932 GObject *storage = NULL;
7934 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
7937 g_object_set (storage, "size-time", latency_ns, NULL);
7939 g_object_unref (storage);
7944 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
7945 const GValue * value, GParamSpec * pspec)
7947 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7950 case PROP_STUN_SERVER:
7951 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
7952 g_value_get_string (value));
7954 case PROP_TURN_SERVER:
7955 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
7956 g_value_get_string (value));
7958 case PROP_BUNDLE_POLICY:
7959 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
7960 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
7962 webrtc->bundle_policy = g_value_get_enum (value);
7965 case PROP_ICE_TRANSPORT_POLICY:
7966 webrtc->ice_transport_policy = g_value_get_enum (value);
7967 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
7968 webrtc->ice_transport_policy ==
7969 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
7972 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
7973 webrtc->priv->jb_latency = g_value_get_uint (value);
7974 _update_rtpstorage_latency (webrtc);
7977 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7983 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
7984 GValue * value, GParamSpec * pspec)
7986 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7990 case PROP_CONNECTION_STATE:
7991 g_value_set_enum (value, webrtc->peer_connection_state);
7993 case PROP_SIGNALING_STATE:
7994 g_value_set_enum (value, webrtc->signaling_state);
7996 case PROP_ICE_GATHERING_STATE:
7997 g_value_set_enum (value, webrtc->ice_gathering_state);
7999 case PROP_ICE_CONNECTION_STATE:
8000 g_value_set_enum (value, webrtc->ice_connection_state);
8002 case PROP_LOCAL_DESCRIPTION:
8003 if (webrtc->pending_local_description)
8004 g_value_set_boxed (value, webrtc->pending_local_description);
8005 else if (webrtc->current_local_description)
8006 g_value_set_boxed (value, webrtc->current_local_description);
8008 g_value_set_boxed (value, NULL);
8010 case PROP_CURRENT_LOCAL_DESCRIPTION:
8011 g_value_set_boxed (value, webrtc->current_local_description);
8013 case PROP_PENDING_LOCAL_DESCRIPTION:
8014 g_value_set_boxed (value, webrtc->pending_local_description);
8016 case PROP_REMOTE_DESCRIPTION:
8017 if (webrtc->pending_remote_description)
8018 g_value_set_boxed (value, webrtc->pending_remote_description);
8019 else if (webrtc->current_remote_description)
8020 g_value_set_boxed (value, webrtc->current_remote_description);
8022 g_value_set_boxed (value, NULL);
8024 case PROP_CURRENT_REMOTE_DESCRIPTION:
8025 g_value_set_boxed (value, webrtc->current_remote_description);
8027 case PROP_PENDING_REMOTE_DESCRIPTION:
8028 g_value_set_boxed (value, webrtc->pending_remote_description);
8030 case PROP_STUN_SERVER:
8031 g_value_take_string (value,
8032 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
8034 case PROP_TURN_SERVER:
8035 g_value_take_string (value,
8036 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
8038 case PROP_BUNDLE_POLICY:
8039 g_value_set_enum (value, webrtc->bundle_policy);
8041 case PROP_ICE_TRANSPORT_POLICY:
8042 g_value_set_enum (value, webrtc->ice_transport_policy);
8044 case PROP_ICE_AGENT:
8045 g_value_set_object (value, webrtc->priv->ice);
8048 g_value_set_uint (value, webrtc->priv->jb_latency);
8050 case PROP_SCTP_TRANSPORT:
8051 g_value_set_object (value, webrtc->priv->sctp_transport);
8054 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8061 gst_webrtc_bin_constructed (GObject * object)
8063 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8066 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
8067 webrtc->priv->ice = gst_webrtc_ice_new (name);
8069 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
8070 (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
8074 G_OBJECT_CLASS (parent_class)->constructed (object);
8078 _free_pending_pad (GstPad * pad)
8080 gst_object_unref (pad);
8084 gst_webrtc_bin_dispose (GObject * object)
8086 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8088 if (webrtc->priv->ice)
8089 gst_object_unref (webrtc->priv->ice);
8090 webrtc->priv->ice = NULL;
8092 if (webrtc->priv->ice_stream_map)
8093 g_array_free (webrtc->priv->ice_stream_map, TRUE);
8094 webrtc->priv->ice_stream_map = NULL;
8096 g_clear_object (&webrtc->priv->sctp_transport);
8098 G_OBJECT_CLASS (parent_class)->dispose (object);
8102 gst_webrtc_bin_finalize (GObject * object)
8104 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8106 if (webrtc->priv->transports)
8107 g_ptr_array_free (webrtc->priv->transports, TRUE);
8108 webrtc->priv->transports = NULL;
8110 if (webrtc->priv->transceivers)
8111 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
8112 webrtc->priv->transceivers = NULL;
8114 if (webrtc->priv->data_channels)
8115 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
8116 webrtc->priv->data_channels = NULL;
8118 if (webrtc->priv->pending_data_channels)
8119 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
8120 webrtc->priv->pending_data_channels = NULL;
8122 if (webrtc->priv->pending_remote_ice_candidates)
8123 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
8124 webrtc->priv->pending_remote_ice_candidates = NULL;
8126 if (webrtc->priv->pending_local_ice_candidates)
8127 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
8128 webrtc->priv->pending_local_ice_candidates = NULL;
8130 if (webrtc->priv->pending_pads)
8131 g_list_free_full (webrtc->priv->pending_pads,
8132 (GDestroyNotify) _free_pending_pad);
8133 webrtc->priv->pending_pads = NULL;
8135 if (webrtc->priv->pending_sink_transceivers)
8136 g_list_free_full (webrtc->priv->pending_sink_transceivers,
8137 (GDestroyNotify) gst_object_unref);
8138 webrtc->priv->pending_sink_transceivers = NULL;
8140 if (webrtc->current_local_description)
8141 gst_webrtc_session_description_free (webrtc->current_local_description);
8142 webrtc->current_local_description = NULL;
8143 if (webrtc->pending_local_description)
8144 gst_webrtc_session_description_free (webrtc->pending_local_description);
8145 webrtc->pending_local_description = NULL;
8147 if (webrtc->current_remote_description)
8148 gst_webrtc_session_description_free (webrtc->current_remote_description);
8149 webrtc->current_remote_description = NULL;
8150 if (webrtc->pending_remote_description)
8151 gst_webrtc_session_description_free (webrtc->pending_remote_description);
8152 webrtc->pending_remote_description = NULL;
8154 if (webrtc->priv->last_generated_answer)
8155 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
8156 webrtc->priv->last_generated_answer = NULL;
8157 if (webrtc->priv->last_generated_offer)
8158 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
8159 webrtc->priv->last_generated_offer = NULL;
8161 g_mutex_clear (DC_GET_LOCK (webrtc));
8162 g_mutex_clear (ICE_GET_LOCK (webrtc));
8163 g_mutex_clear (PC_GET_LOCK (webrtc));
8164 g_cond_clear (PC_GET_COND (webrtc));
8166 G_OBJECT_CLASS (parent_class)->finalize (object);
8170 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
8172 GObjectClass *gobject_class = (GObjectClass *) klass;
8173 GstElementClass *element_class = (GstElementClass *) klass;
8175 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
8176 element_class->release_pad = gst_webrtc_bin_release_pad;
8177 element_class->change_state = gst_webrtc_bin_change_state;
8179 gst_element_class_add_static_pad_template_with_gtype (element_class,
8180 &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
8181 gst_element_class_add_static_pad_template (element_class, &src_template);
8183 gst_element_class_set_metadata (element_class, "WebRTC Bin",
8184 "Filter/Network/WebRTC", "A bin for webrtc connections",
8185 "Matthew Waters <matthew@centricular.com>");
8187 gobject_class->constructed = gst_webrtc_bin_constructed;
8188 gobject_class->get_property = gst_webrtc_bin_get_property;
8189 gobject_class->set_property = gst_webrtc_bin_set_property;
8190 gobject_class->dispose = gst_webrtc_bin_dispose;
8191 gobject_class->finalize = gst_webrtc_bin_finalize;
8193 g_object_class_install_property (gobject_class,
8194 PROP_LOCAL_DESCRIPTION,
8195 g_param_spec_boxed ("local-description", "Local Description",
8196 "The local SDP description in use for this connection. "
8197 "Favours a pending description over the current description",
8198 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8199 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8201 g_object_class_install_property (gobject_class,
8202 PROP_CURRENT_LOCAL_DESCRIPTION,
8203 g_param_spec_boxed ("current-local-description",
8204 "Current Local Description",
8205 "The local description that was successfully negotiated the last time "
8206 "the connection transitioned into the stable state",
8207 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8208 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8210 g_object_class_install_property (gobject_class,
8211 PROP_PENDING_LOCAL_DESCRIPTION,
8212 g_param_spec_boxed ("pending-local-description",
8213 "Pending Local Description",
8214 "The local description that is in the process of being negotiated plus "
8215 "any local candidates that have been generated by the ICE Agent since the "
8216 "offer or answer was created",
8217 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8218 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8220 g_object_class_install_property (gobject_class,
8221 PROP_REMOTE_DESCRIPTION,
8222 g_param_spec_boxed ("remote-description", "Remote Description",
8223 "The remote SDP description to use for this connection. "
8224 "Favours a pending description over the current description",
8225 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8226 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8228 g_object_class_install_property (gobject_class,
8229 PROP_CURRENT_REMOTE_DESCRIPTION,
8230 g_param_spec_boxed ("current-remote-description",
8231 "Current Remote Description",
8232 "The last remote description that was successfully negotiated the last "
8233 "time the connection transitioned into the stable state plus any remote "
8234 "candidates that have been supplied via addIceCandidate() since the offer "
8235 "or answer was created",
8236 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8237 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8239 g_object_class_install_property (gobject_class,
8240 PROP_PENDING_REMOTE_DESCRIPTION,
8241 g_param_spec_boxed ("pending-remote-description",
8242 "Pending Remote Description",
8243 "The remote description that is in the process of being negotiated, "
8244 "complete with any remote candidates that have been supplied via "
8245 "addIceCandidate() since the offer or answer was created",
8246 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8247 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8249 g_object_class_install_property (gobject_class,
8251 g_param_spec_string ("stun-server", "STUN Server",
8252 "The STUN server of the form stun://hostname:port",
8253 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8255 g_object_class_install_property (gobject_class,
8257 g_param_spec_string ("turn-server", "TURN Server",
8258 "The TURN server of the form turn(s)://username:password@host:port. "
8259 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
8260 "if you wish to use multiple TURN servers",
8261 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8263 g_object_class_install_property (gobject_class,
8264 PROP_CONNECTION_STATE,
8265 g_param_spec_enum ("connection-state", "Connection State",
8266 "The overall connection state of this element",
8267 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
8268 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
8269 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8271 g_object_class_install_property (gobject_class,
8272 PROP_SIGNALING_STATE,
8273 g_param_spec_enum ("signaling-state", "Signaling State",
8274 "The signaling state of this element",
8275 GST_TYPE_WEBRTC_SIGNALING_STATE,
8276 GST_WEBRTC_SIGNALING_STATE_STABLE,
8277 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8279 g_object_class_install_property (gobject_class,
8280 PROP_ICE_CONNECTION_STATE,
8281 g_param_spec_enum ("ice-connection-state", "ICE connection state",
8282 "The collective connection state of all ICETransport's",
8283 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
8284 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
8285 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8287 g_object_class_install_property (gobject_class,
8288 PROP_ICE_GATHERING_STATE,
8289 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
8290 "The collective gathering state of all ICETransport's",
8291 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
8292 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
8293 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8295 g_object_class_install_property (gobject_class,
8297 g_param_spec_enum ("bundle-policy", "Bundle Policy",
8298 "The policy to apply for bundling",
8299 GST_TYPE_WEBRTC_BUNDLE_POLICY,
8300 GST_WEBRTC_BUNDLE_POLICY_NONE,
8301 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8303 g_object_class_install_property (gobject_class,
8304 PROP_ICE_TRANSPORT_POLICY,
8305 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
8306 "The policy to apply for ICE transport",
8307 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
8308 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
8309 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8311 g_object_class_install_property (gobject_class,
8313 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
8314 "The WebRTC ICE agent",
8315 GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8318 * GstWebRTCBin:latency:
8320 * Default duration to buffer in the jitterbuffers (in ms)
8325 g_object_class_install_property (gobject_class,
8327 g_param_spec_uint ("latency", "Latency",
8328 "Default duration to buffer in the jitterbuffers (in ms)",
8329 0, G_MAXUINT, DEFAULT_JB_LATENCY,
8330 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8333 * GstWebRTCBin:sctp-transport:
8335 * The WebRTC SCTP Transport
8339 g_object_class_install_property (gobject_class,
8340 PROP_SCTP_TRANSPORT,
8341 g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
8342 "The WebRTC SCTP Transport",
8343 GST_TYPE_WEBRTC_SCTP_TRANSPORT,
8344 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8347 * GstWebRTCBin::create-offer:
8348 * @object: the #webrtcbin
8349 * @options: (nullable): create-offer options
8350 * @promise: a #GstPromise which will contain the offer
8352 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
8353 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
8354 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8355 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
8356 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8359 * GstWebRTCBin::create-answer:
8360 * @object: the #webrtcbin
8361 * @options: (nullable): create-answer options
8362 * @promise: a #GstPromise which will contain the answer
8364 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
8365 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
8366 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8367 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
8368 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8371 * GstWebRTCBin::set-local-description:
8372 * @object: the #GstWebRTCBin
8373 * @desc: a #GstWebRTCSessionDescription description
8374 * @promise: (nullable): a #GstPromise to be notified when it's set
8376 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
8377 g_signal_new_class_handler ("set-local-description",
8378 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8379 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
8380 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8383 * GstWebRTCBin::set-remote-description:
8384 * @object: the #GstWebRTCBin
8385 * @desc: a #GstWebRTCSessionDescription description
8386 * @promise: (nullable): a #GstPromise to be notified when it's set
8388 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
8389 g_signal_new_class_handler ("set-remote-description",
8390 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8391 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
8392 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8395 * GstWebRTCBin::add-ice-candidate:
8396 * @object: the #webrtcbin
8397 * @mline_index: the index of the media description in the SDP
8398 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
8401 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
8402 g_signal_new_class_handler ("add-ice-candidate",
8403 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8404 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
8405 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8408 * GstWebRTCBin::get-stats:
8409 * @object: the #webrtcbin
8410 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
8411 * @promise: a #GstPromise for the result
8413 * The @promise will contain the result of retrieving the session statistics.
8414 * The structure will be named 'application/x-webrtc-stats and contain the
8415 * following based on the webrtc-stats spec available from
8416 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
8417 * and is constantly changing these statistics may be changed to fit with
8420 * Each field key is a unique identifier for each RTCStats
8421 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
8422 * GstStructure) in the RTCStatsReport
8423 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
8424 * field in the RTCStats subclass is outlined below.
8426 * Each statistics structure contains the following values as defined by
8427 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
8429 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
8430 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
8431 * "id" G_TYPE_STRING unique identifier
8433 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
8435 * "payload-type" G_TYPE_UINT the rtp payload number in use
8436 * "clock-rate" G_TYPE_UINT the rtp clock-rate
8438 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
8440 * "ssrc" G_TYPE_STRING the rtp sequence src in use
8441 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
8442 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
8444 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
8446 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
8447 * "packets-lost" G_TYPE_INT64 number of packets lost
8448 * "packets-discarded" G_TYPE_UINT64 number of packets discarded
8449 * "packets-repaired" G_TYPE_UINT64 number of packets repaired
8450 * "jitter" G_TYPE_DOUBLE packet jitter measured in seconds
8452 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
8454 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
8455 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
8456 * "packets-duplicated" G_TYPE_UINT64 number of packets duplicated
8457 * "fir-count" G_TYPE_UINT FIR packets sent by the receiver
8458 * "pli-count" G_TYPE_UINT PLI packets sent by the receiver
8459 * "nack-count" G_TYPE_UINT NACK packets sent by the receiver
8461 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
8463 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
8464 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
8465 * "fraction-lost" G_TYPE_DOUBLE fraction packet loss
8467 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
8469 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
8470 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
8472 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
8474 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats (optional since 1.22)
8475 * "fir-count" G_TYPE_UINT FIR packets received by the sender
8476 * "pli-count" G_TYPE_UINT PLI packets received by the sender
8477 * "nack-count" G_TYPE_UINT NACK packets received by the sender
8479 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
8481 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
8482 * "remote-timestamp" G_TYPE_DOUBLE remote timestamp the statistics were sent by the remote
8484 * RTCIceCandidateStats supported fields (https://www.w3.org/TR/webrtc-stats/#icecandidate-dict*) (Since: 1.22)
8486 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
8487 * "address" G_TYPE_STRING address of the candidate, allowing for IPv4, IPv6 and FQDNs
8488 * "port" G_TYPE_UINT port number of the candidate
8489 * "candidate-type" G_TYPE_STRING RTCIceCandidateType
8490 * "priority" G_TYPE_UINT64 calculated as defined in RFC 5245
8491 * "protocol" G_TYPE_STRING Either "udp" or "tcp". Based on the "transport" defined in RFC 5245
8492 * "relay-protocol" G_TYPE_STRING protocol used by the endpoint to communicate with the TURN server. Only present for local candidates. Either "udp", "tcp" or "tls"
8493 * "url" G_TYPE_STRING URL of the ICE server from which the candidate was obtained. Only present for local candidates
8495 * RTCIceCandidatePairStats supported fields (https://www.w3.org/TR/webrtc-stats/#candidatepair-dict*) (Since: 1.22)
8497 * "local-candidate-id" G_TYPE_STRING unique identifier that is associated to the object that was inspected to produce the RTCIceCandidateStats for the local candidate associated with this candidate pair.
8498 * "remote-candidate-id" G_TYPE_STRING unique identifier that is associated to the object that was inspected to produce the RTCIceCandidateStats for the remote candidate associated with this candidate pair.
8500 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
8501 g_signal_new_class_handler ("get-stats",
8502 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8503 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
8504 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
8507 * GstWebRTCBin::on-negotiation-needed:
8508 * @object: the #webrtcbin
8510 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
8511 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
8512 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
8515 * GstWebRTCBin::on-ice-candidate:
8516 * @object: the #webrtcbin
8517 * @mline_index: the index of the media description in the SDP
8518 * @candidate: the ICE candidate
8520 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
8521 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
8522 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8523 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8526 * GstWebRTCBin::on-new-transceiver:
8527 * @object: the #webrtcbin
8528 * @candidate: the new #GstWebRTCRTPTransceiver
8530 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
8531 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
8532 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8533 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
8536 * GstWebRTCBin::on-data-channel:
8537 * @object: the #GstWebRTCBin
8538 * @channel: the new `GstWebRTCDataChannel`
8540 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
8541 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
8542 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8543 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
8546 * GstWebRTCBin::prepare-data-channel:
8547 * @object: the #GstWebRTCBin
8548 * @channel: the new `GstWebRTCDataChannel`
8549 * @is_local: Whether this channel is local or remote
8551 * Allows data-channel consumers to configure signal handlers on a newly
8552 * created data-channel, before any data or state change has been notified.
8556 gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL] =
8557 g_signal_new ("prepare-data-channel", G_TYPE_FROM_CLASS (klass),
8558 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2,
8559 GST_TYPE_WEBRTC_DATA_CHANNEL, G_TYPE_BOOLEAN);
8562 * GstWebRTCBin::add-transceiver:
8563 * @object: the #webrtcbin
8564 * @direction: the direction of the new transceiver
8565 * @caps: (allow none): the codec preferences for this transceiver
8567 * Returns: the new #GstWebRTCRTPTransceiver
8569 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
8570 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
8571 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8572 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
8573 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
8574 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
8577 * GstWebRTCBin::get-transceivers:
8578 * @object: the #webrtcbin
8580 * Returns: a #GArray of #GstWebRTCRTPTransceivers
8582 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
8583 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
8584 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8585 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
8589 * GstWebRTCBin::get-transceiver:
8590 * @object: the #GstWebRTCBin
8591 * @idx: The index of the transceiver
8593 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
8596 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
8597 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
8598 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8599 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
8600 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
8603 * GstWebRTCBin::add-turn-server:
8604 * @object: the #GstWebRTCBin
8605 * @uri: The uri of the server of the form turn(s)://username:password@host:port
8607 * Add a turn server to obtain ICE candidates from
8609 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
8610 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
8611 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8612 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
8613 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
8616 * GstWebRTCBin::create-data-channel:
8617 * @object: the #GstWebRTCBin
8618 * @label: the label for the data channel
8619 * @options: a #GstStructure of options for creating the data channel
8621 * The options dictionary is the same format as the RTCDataChannelInit
8622 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
8623 * and reproduced below
8625 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
8626 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
8627 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
8628 * protocol G_TYPE_STRING The subprotocol used by this channel
8629 * 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.
8630 * id G_TYPE_INT Override the default identifier selection of this channel
8631 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
8633 * Returns: (transfer full): a new data channel object
8635 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
8636 g_signal_new_class_handler ("create-data-channel",
8637 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8638 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
8639 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
8641 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
8642 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
8646 _unparent_and_unref (GObject * object)
8648 GstObject *obj = GST_OBJECT (object);
8650 GST_OBJECT_PARENT (obj) = NULL;
8652 gst_object_unref (obj);
8656 _transport_free (GObject * object)
8658 TransportStream *stream = (TransportStream *) object;
8659 GstWebRTCBin *webrtc;
8661 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
8663 if (stream->transport) {
8664 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
8665 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
8668 gst_object_unref (object);
8672 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
8674 /* Set SINK/SRC flags as webrtcbin can act as one depending on the
8675 * SDP later. Without setting this here already, surrounding bins might not
8676 * notice this and the pipeline configuration might become inconsistent,
8677 * e.g. with regards to latency.
8678 * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
8680 gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
8681 GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
8682 GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
8684 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
8685 g_mutex_init (PC_GET_LOCK (webrtc));
8686 g_cond_init (PC_GET_COND (webrtc));
8688 g_mutex_init (ICE_GET_LOCK (webrtc));
8689 g_mutex_init (DC_GET_LOCK (webrtc));
8691 webrtc->rtpbin = _create_rtpbin (webrtc);
8692 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
8694 webrtc->priv->transceivers =
8695 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
8696 webrtc->priv->transports =
8697 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
8699 webrtc->priv->data_channels =
8700 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
8702 webrtc->priv->pending_data_channels =
8703 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
8705 webrtc->priv->ice_stream_map =
8706 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
8707 webrtc->priv->pending_remote_ice_candidates =
8708 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
8709 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
8710 (GDestroyNotify) _clear_ice_candidate_item);
8712 webrtc->priv->pending_local_ice_candidates =
8713 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
8714 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
8715 (GDestroyNotify) _clear_ice_candidate_item);
8717 /* we start off closed until we move to READY */
8718 webrtc->priv->is_closed = TRUE;
8719 webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;