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"
35 #include <gst/webrtc/nice/nice.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"));
220 _gst_element_accumulator (GSignalInvocationHint * ihint,
221 GValue * return_accu, const GValue * handler_return, gpointer dummy)
225 element = g_value_get_object (handler_return);
226 GST_DEBUG ("got element %" GST_PTR_FORMAT, element);
228 g_value_set_object (return_accu, element);
230 /* stop emission if we have an element */
231 return (element == NULL);
234 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
237 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
238 GValue * value, GParamSpec * pspec)
240 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
243 case PROP_PAD_TRANSCEIVER:
244 g_value_set_object (value, pad->trans);
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253 gst_webrtc_bin_pad_finalize (GObject * object)
255 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
257 gst_clear_object (&pad->trans);
258 gst_clear_caps (&pad->received_caps);
259 g_clear_pointer (&pad->msid, g_free);
261 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
265 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
267 GObjectClass *gobject_class = (GObjectClass *) klass;
269 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
270 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
272 g_object_class_install_property (gobject_class,
273 PROP_PAD_TRANSCEIVER,
274 g_param_spec_object ("transceiver", "Transceiver",
275 "Transceiver associated with this pad",
276 GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
277 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
281 gst_webrtc_bin_pad_update_tos_event (GstWebRTCBinPad * wpad)
283 WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
285 if (wpad->received_caps && trans->parent.mid) {
286 GstPad *pad = GST_PAD (wpad);
288 gst_event_take (&trans->tos_event,
289 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
290 gst_structure_new ("GstWebRtcBinUpdateTos", "mid", G_TYPE_STRING,
291 trans->parent.mid, NULL)));
293 GST_DEBUG_OBJECT (pad, "sending new tos event %" GST_PTR_FORMAT,
295 gst_pad_send_event (pad, gst_event_ref (trans->tos_event));
300 _get_pending_sink_transceiver (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
304 for (ret = webrtc->priv->pending_sink_transceivers; ret; ret = ret->next) {
305 if (ret->data == pad)
313 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
315 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
316 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
317 gboolean check_negotiation = FALSE;
319 if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
322 gst_event_parse_caps (event, &caps);
323 check_negotiation = (!wpad->received_caps
324 || !gst_caps_is_equal (wpad->received_caps, caps));
325 gst_caps_replace (&wpad->received_caps, caps);
327 GST_DEBUG_OBJECT (parent,
328 "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
329 GST_PTR_FORMAT, pad, check_negotiation, caps);
331 if (check_negotiation) {
332 gst_webrtc_bin_pad_update_tos_event (wpad);
335 /* A remote description might have been set while the pad hadn't
336 * yet received caps, delaying the connection of the input stream
340 GST_OBJECT_LOCK (wpad->trans);
341 if (wpad->trans->current_direction ==
342 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY
343 || wpad->trans->current_direction ==
344 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
345 GList *pending = _get_pending_sink_transceiver (webrtc, wpad);
348 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
349 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
350 wpad->trans, wpad->received_caps);
351 _connect_input_stream (webrtc, wpad);
352 gst_pad_remove_probe (GST_PAD (pad), wpad->block_id);
354 gst_object_unref (pending->data);
355 webrtc->priv->pending_sink_transceivers =
356 g_list_delete_link (webrtc->priv->pending_sink_transceivers,
360 GST_OBJECT_UNLOCK (wpad->trans);
363 } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
364 check_negotiation = TRUE;
367 if (check_negotiation) {
369 _update_need_negotiation (webrtc);
373 return gst_pad_event_default (pad, parent, event);
377 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
379 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
380 gboolean ret = FALSE;
382 switch (GST_QUERY_TYPE (query)) {
383 case GST_QUERY_ACCEPT_CAPS:
384 GST_OBJECT_LOCK (wpad->trans);
385 if (wpad->trans->codec_preferences) {
388 gst_query_parse_accept_caps (query, &caps);
390 gst_query_set_accept_caps_result (query,
391 gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
394 GST_OBJECT_UNLOCK (wpad->trans);
399 GstCaps *codec_preferences = NULL;
401 GST_OBJECT_LOCK (wpad->trans);
402 if (wpad->trans->codec_preferences)
403 codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
404 GST_OBJECT_UNLOCK (wpad->trans);
406 if (codec_preferences) {
407 GstCaps *filter = NULL;
408 GstCaps *filter_prefs = NULL;
411 gst_query_parse_caps (query, &filter);
414 filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
415 GST_CAPS_INTERSECT_FIRST);
416 gst_caps_unref (codec_preferences);
418 filter_prefs = codec_preferences;
421 target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
425 result = gst_pad_query_caps (target, filter_prefs);
426 gst_query_set_caps_result (query, result);
427 gst_caps_unref (result);
429 gst_object_unref (target);
431 gst_query_set_caps_result (query, filter_prefs);
434 gst_caps_unref (filter_prefs);
446 return gst_pad_query_default (pad, parent, query);
451 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
455 static GstWebRTCBinPad *
456 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction,
459 GstWebRTCBinPad *pad;
460 GstPadTemplate *template;
463 if (direction == GST_PAD_SINK) {
464 template = gst_static_pad_template_get (&sink_template);
465 pad_type = GST_TYPE_WEBRTC_BIN_SINK_PAD;
466 } else if (direction == GST_PAD_SRC) {
467 template = gst_static_pad_template_get (&src_template);
468 pad_type = GST_TYPE_WEBRTC_BIN_SRC_PAD;
470 g_assert_not_reached ();
474 g_object_new (pad_type, "name", name, "direction",
475 direction, "template", template, NULL);
476 gst_object_unref (template);
480 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
481 direction == GST_PAD_SRC ? "src" : "sink");
487 PROP_SINK_PAD_MSID = 1,
491 * GstWebRTCBinSinkPad:
495 struct _GstWebRTCBinSinkPad
500 G_DEFINE_TYPE (GstWebRTCBinSinkPad, gst_webrtc_bin_sink_pad,
501 GST_TYPE_WEBRTC_BIN_PAD);
504 gst_webrtc_bin_sink_pad_get_property (GObject * object, guint prop_id,
505 GValue * value, GParamSpec * pspec)
507 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
510 case PROP_SINK_PAD_MSID:
511 g_value_set_string (value, pad->msid);
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
520 gst_webrtc_bin_sink_pad_set_property (GObject * object, guint prop_id,
521 const GValue * value, GParamSpec * pspec)
523 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
526 case PROP_SINK_PAD_MSID:
528 pad->msid = g_value_dup_string (value);
531 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
537 gst_webrtc_bin_sink_pad_class_init (GstWebRTCBinSinkPadClass * klass)
539 GObjectClass *gobject_class = (GObjectClass *) klass;
541 gobject_class->get_property = gst_webrtc_bin_sink_pad_get_property;
542 gobject_class->set_property = gst_webrtc_bin_sink_pad_set_property;
545 * GstWebRTCBinSinkPad:msid:
547 * The MediaStream Identifier to use for this pad (MediaStreamTrack).
548 * Fallback is the RTP SDES cname value if not provided.
552 g_object_class_install_property (gobject_class,
554 g_param_spec_string ("msid", "MSID",
555 "Local MediaStream ID to use for this pad (NULL = unset)", NULL,
556 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
560 gst_webrtc_bin_sink_pad_init (GstWebRTCBinSinkPad * pad)
562 gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
563 gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
568 PROP_SRC_PAD_MSID = 1,
572 * GstWebRTCBinSrcPad:
576 struct _GstWebRTCBinSrcPad
581 G_DEFINE_TYPE (GstWebRTCBinSrcPad, gst_webrtc_bin_src_pad,
582 GST_TYPE_WEBRTC_BIN_PAD);
585 gst_webrtc_bin_src_pad_get_property (GObject * object, guint prop_id,
586 GValue * value, GParamSpec * pspec)
588 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
591 case PROP_SRC_PAD_MSID:
592 g_value_set_string (value, pad->msid);
595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601 gst_webrtc_bin_src_pad_class_init (GstWebRTCBinSrcPadClass * klass)
603 GObjectClass *gobject_class = (GObjectClass *) klass;
605 gobject_class->get_property = gst_webrtc_bin_src_pad_get_property;
608 * GstWebRTCBinSrcPad:msid:
610 * The MediaStream Identifier the remote peer used for this pad (MediaStreamTrack).
611 * Will be NULL if not advertised in the remote SDP.
615 g_object_class_install_property (gobject_class,
617 g_param_spec_string ("msid", "MSID",
618 "Remote MediaStream ID in use for this pad (NULL = not advertised)",
619 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
623 gst_webrtc_bin_src_pad_init (GstWebRTCBinSrcPad * pad)
627 #define gst_webrtc_bin_parent_class parent_class
628 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
629 G_ADD_PRIVATE (GstWebRTCBin)
630 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
631 "webrtcbin element"););
637 CREATE_ANSWER_SIGNAL,
638 SET_LOCAL_DESCRIPTION_SIGNAL,
639 SET_REMOTE_DESCRIPTION_SIGNAL,
640 ADD_ICE_CANDIDATE_SIGNAL,
641 ON_NEGOTIATION_NEEDED_SIGNAL,
642 ON_ICE_CANDIDATE_SIGNAL,
643 ON_NEW_TRANSCEIVER_SIGNAL,
645 ADD_TRANSCEIVER_SIGNAL,
646 GET_TRANSCEIVER_SIGNAL,
647 GET_TRANSCEIVERS_SIGNAL,
648 ADD_TURN_SERVER_SIGNAL,
649 CREATE_DATA_CHANNEL_SIGNAL,
650 ON_DATA_CHANNEL_SIGNAL,
651 PREPARE_DATA_CHANNEL_SIGNAL,
659 PROP_CONNECTION_STATE,
660 PROP_SIGNALING_STATE,
661 PROP_ICE_GATHERING_STATE,
662 PROP_ICE_CONNECTION_STATE,
663 PROP_LOCAL_DESCRIPTION,
664 PROP_CURRENT_LOCAL_DESCRIPTION,
665 PROP_PENDING_LOCAL_DESCRIPTION,
666 PROP_REMOTE_DESCRIPTION,
667 PROP_CURRENT_REMOTE_DESCRIPTION,
668 PROP_PENDING_REMOTE_DESCRIPTION,
672 PROP_ICE_TRANSPORT_POLICY,
677 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
679 PROP_DROP_PROBABILITY_SENDER,
680 PROP_DROP_PROBABILITY_RECEIVER
684 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
689 GstWebRTCICEStream *stream;
692 /* FIXME: locking? */
694 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
698 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
699 IceStreamItem *item =
700 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
702 if (item->session_id == session_id) {
703 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
704 "session %u", item->stream, session_id);
709 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
715 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
716 GstWebRTCICEStream * stream)
718 IceStreamItem item = { session_id, stream };
720 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
721 "session %u", stream, session_id);
722 g_array_append_val (webrtc->priv->ice_stream_map, item);
725 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
728 static GstWebRTCRTPTransceiver *
729 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
730 FindTransceiverFunc func)
734 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
735 GstWebRTCRTPTransceiver *transceiver =
736 g_ptr_array_index (webrtc->priv->transceivers, i);
738 if (func (transceiver, data))
746 transceiver_match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
748 return g_strcmp0 (trans->mid, mid) == 0;
752 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
757 return trans->mline == *mline;
760 static GstWebRTCRTPTransceiver *
761 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
763 GstWebRTCRTPTransceiver *trans;
765 trans = _find_transceiver (webrtc, &mlineindex,
766 (FindTransceiverFunc) transceiver_match_for_mline);
768 GST_TRACE_OBJECT (webrtc,
769 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
775 static GstWebRTCRTPTransceiver *
776 _find_transceiver_for_mid (GstWebRTCBin * webrtc, const char *mid)
778 GstWebRTCRTPTransceiver *trans;
780 trans = _find_transceiver (webrtc, mid,
781 (FindTransceiverFunc) transceiver_match_for_mid);
783 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT " for "
784 "mid %s", trans, mid);
789 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
792 static TransportStream *
793 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
794 FindTransportFunc func)
798 for (i = 0; i < webrtc->priv->transports->len; i++) {
799 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
801 if (func (stream, data))
809 match_stream_for_session (TransportStream * trans, guint * session)
811 return trans->session_id == *session;
814 static TransportStream *
815 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
817 TransportStream *stream;
819 stream = _find_transport (webrtc, &session_id,
820 (FindTransportFunc) match_stream_for_session);
822 GST_TRACE_OBJECT (webrtc,
823 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
828 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
830 static GstWebRTCBinPad *
831 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
833 GstElement *element = GST_ELEMENT (webrtc);
836 GST_OBJECT_LOCK (webrtc);
838 for (; l; l = g_list_next (l)) {
839 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
841 if (func (l->data, data)) {
842 gst_object_ref (l->data);
843 GST_OBJECT_UNLOCK (webrtc);
848 l = webrtc->priv->pending_pads;
849 for (; l; l = g_list_next (l)) {
850 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
852 if (func (l->data, data)) {
853 gst_object_ref (l->data);
854 GST_OBJECT_UNLOCK (webrtc);
858 GST_OBJECT_UNLOCK (webrtc);
863 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
866 static WebRTCDataChannel *
867 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
868 FindDataChannelFunc func)
872 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
873 WebRTCDataChannel *channel =
874 g_ptr_array_index (webrtc->priv->data_channels, i);
876 if (func (channel, data))
884 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
886 return channel->parent.id == *id;
889 /* always called with dc_lock held */
890 static WebRTCDataChannel *
891 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
893 WebRTCDataChannel *channel;
895 channel = _find_data_channel (webrtc, &id,
896 (FindDataChannelFunc) data_channel_match_for_id);
898 GST_TRACE_OBJECT (webrtc,
899 "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
905 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
907 GST_OBJECT_LOCK (webrtc);
908 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
909 GST_OBJECT_UNLOCK (webrtc);
913 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
915 gboolean ret = FALSE;
918 GST_OBJECT_LOCK (webrtc);
919 l = g_list_find (webrtc->priv->pending_pads, pad);
921 webrtc->priv->pending_pads =
922 g_list_remove_link (webrtc->priv->pending_pads, l);
926 GST_OBJECT_UNLOCK (webrtc);
932 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
934 _remove_pending_pad (webrtc, pad);
936 if (webrtc->priv->running)
937 gst_pad_set_active (GST_PAD (pad), TRUE);
938 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
942 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
944 _remove_pending_pad (webrtc, pad);
946 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
951 GstPadDirection direction;
956 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
958 return GST_PAD_DIRECTION (pad) == match->direction
959 && pad->trans->mline == match->mline;
962 static GstWebRTCBinPad *
963 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
966 MLineMatch m = { direction, mline };
968 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
973 GstPadDirection direction;
974 GstWebRTCRTPTransceiver *trans;
978 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
980 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
983 static GstWebRTCBinPad *
984 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
985 GstWebRTCRTPTransceiver * trans)
987 TransMatch m = { direction, trans };
989 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
994 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
1002 GstWebRTCRTPTransceiverDirection direction;
1007 mid_ssrc_match_for_ssrc (SsrcMapItem * entry, const struct SsrcMatch *match)
1009 return entry->direction == match->direction && entry->ssrc == match->ssrc;
1013 mid_ssrc_remove_ssrc (SsrcMapItem * item, const struct SsrcMatch *match)
1015 return !mid_ssrc_match_for_ssrc (item, match);
1018 static SsrcMapItem *
1019 find_mid_ssrc_for_ssrc (GstWebRTCBin * webrtc,
1020 GstWebRTCRTPTransceiverDirection direction, guint rtp_session, guint ssrc)
1022 TransportStream *stream = _find_transport_for_session (webrtc, rtp_session);
1023 struct SsrcMatch m = { direction, ssrc };
1028 return transport_stream_find_ssrc_map_item (stream, &m,
1029 (FindSsrcMapFunc) mid_ssrc_match_for_ssrc);
1032 static SsrcMapItem *
1033 find_or_add_ssrc_map_item (GstWebRTCBin * webrtc,
1034 GstWebRTCRTPTransceiverDirection direction, guint rtp_session, guint ssrc,
1037 TransportStream *stream = _find_transport_for_session (webrtc, rtp_session);
1038 struct SsrcMatch m = { direction, ssrc };
1044 if ((item = transport_stream_find_ssrc_map_item (stream, &m,
1045 (FindSsrcMapFunc) mid_ssrc_match_for_ssrc)))
1048 return transport_stream_add_ssrc_map_item (stream, direction, ssrc,
1053 remove_ssrc_entry_by_ssrc (GstWebRTCBin * webrtc, guint rtp_session, guint ssrc)
1055 TransportStream *stream;
1057 stream = _find_transport_for_session (webrtc, rtp_session);
1059 struct SsrcMatch m =
1060 { GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, ssrc };
1062 transport_stream_filter_ssrc_map_item (stream, &m,
1063 (FindSsrcMapFunc) mid_ssrc_remove_ssrc);
1065 m.direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY;
1066 transport_stream_filter_ssrc_map_item (stream, &m,
1067 (FindSsrcMapFunc) mid_ssrc_remove_ssrc);
1072 _unlock_pc_thread (GMutex * lock)
1074 g_mutex_unlock (lock);
1075 return G_SOURCE_REMOVE;
1079 _gst_pc_thread (GstWebRTCBin * webrtc)
1082 webrtc->priv->main_context = g_main_context_new ();
1083 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
1085 PC_COND_BROADCAST (webrtc);
1086 g_main_context_invoke (webrtc->priv->main_context,
1087 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
1089 /* Having the thread be the thread default GMainContext will break the
1090 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
1092 g_main_loop_run (webrtc->priv->loop);
1094 GST_OBJECT_LOCK (webrtc);
1095 g_main_context_unref (webrtc->priv->main_context);
1096 webrtc->priv->main_context = NULL;
1097 GST_OBJECT_UNLOCK (webrtc);
1100 g_main_loop_unref (webrtc->priv->loop);
1101 webrtc->priv->loop = NULL;
1102 PC_COND_BROADCAST (webrtc);
1109 _start_thread (GstWebRTCBin * webrtc)
1114 name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
1115 webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
1119 while (!webrtc->priv->loop)
1120 PC_COND_WAIT (webrtc);
1121 webrtc->priv->is_closed = FALSE;
1126 _stop_thread (GstWebRTCBin * webrtc)
1128 GST_OBJECT_LOCK (webrtc);
1129 webrtc->priv->is_closed = TRUE;
1130 GST_OBJECT_UNLOCK (webrtc);
1133 g_main_loop_quit (webrtc->priv->loop);
1134 while (webrtc->priv->loop)
1135 PC_COND_WAIT (webrtc);
1138 g_thread_unref (webrtc->priv->thread);
1142 _execute_op (GstWebRTCBinTask * op)
1146 PC_LOCK (op->webrtc);
1147 if (op->webrtc->priv->is_closed) {
1148 PC_UNLOCK (op->webrtc);
1152 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
1153 "webrtcbin is closed. aborting execution.");
1154 GstStructure *s = gst_structure_new ("application/x-gst-promise",
1155 "error", G_TYPE_ERROR, error, NULL);
1157 gst_promise_reply (op->promise, s);
1159 g_clear_error (&error);
1161 GST_DEBUG_OBJECT (op->webrtc,
1162 "Peerconnection is closed, aborting execution");
1166 s = op->op (op->webrtc, op->data);
1168 PC_UNLOCK (op->webrtc);
1171 gst_promise_reply (op->promise, s);
1173 gst_structure_free (s);
1176 return G_SOURCE_REMOVE;
1180 _free_op (GstWebRTCBinTask * op)
1183 op->notify (op->data);
1185 gst_promise_unref (op->promise);
1190 * @promise is for correctly signalling the failure case to the caller when
1191 * the user supplies it. Without passing it in, the promise would never
1192 * be replied to in the case that @webrtc becomes closed between the idle
1193 * source addition and the the execution of the idle source.
1196 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
1197 gpointer data, GDestroyNotify notify, GstPromise * promise)
1199 GstWebRTCBinTask *op;
1203 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
1205 GST_OBJECT_LOCK (webrtc);
1206 if (webrtc->priv->is_closed) {
1207 GST_OBJECT_UNLOCK (webrtc);
1208 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
1213 ctx = g_main_context_ref (webrtc->priv->main_context);
1214 GST_OBJECT_UNLOCK (webrtc);
1216 op = g_new0 (GstWebRTCBinTask, 1);
1217 op->webrtc = webrtc;
1220 op->notify = notify;
1222 op->promise = gst_promise_ref (promise);
1224 source = g_idle_source_new ();
1225 g_source_set_priority (source, G_PRIORITY_DEFAULT);
1226 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
1227 (GDestroyNotify) _free_op);
1228 g_source_attach (source, ctx);
1229 g_source_unref (source);
1230 g_main_context_unref (ctx);
1235 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
1236 static GstWebRTCICEConnectionState
1237 _collate_ice_connection_states (GstWebRTCBin * webrtc)
1239 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
1240 GstWebRTCICEConnectionState any_state = 0;
1241 gboolean all_new_or_closed = TRUE;
1242 gboolean all_completed_or_closed = TRUE;
1243 gboolean all_connected_completed_or_closed = TRUE;
1246 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1247 GstWebRTCRTPTransceiver *rtp_trans =
1248 g_ptr_array_index (webrtc->priv->transceivers, i);
1249 GstWebRTCICETransport *transport;
1250 GstWebRTCICEConnectionState ice_state;
1252 if (rtp_trans->stopped) {
1253 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1257 if (!rtp_trans->mid) {
1258 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1262 transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
1264 /* get transport state */
1265 g_object_get (transport, "state", &ice_state, NULL);
1266 GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
1268 any_state |= (1 << ice_state);
1270 if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
1271 all_new_or_closed = FALSE;
1272 if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
1273 all_completed_or_closed = FALSE;
1274 if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
1275 && ice_state != STATE (CLOSED))
1276 all_connected_completed_or_closed = FALSE;
1279 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
1281 if (webrtc->priv->is_closed) {
1282 GST_TRACE_OBJECT (webrtc, "returning closed");
1283 return STATE (CLOSED);
1285 /* Any of the RTCIceTransports are in the failed state. */
1286 if (any_state & (1 << STATE (FAILED))) {
1287 GST_TRACE_OBJECT (webrtc, "returning failed");
1288 return STATE (FAILED);
1290 /* Any of the RTCIceTransports are in the disconnected state. */
1291 if (any_state & (1 << STATE (DISCONNECTED))) {
1292 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1293 return STATE (DISCONNECTED);
1295 /* All of the RTCIceTransports are in the new or closed state, or there are
1297 if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
1298 GST_TRACE_OBJECT (webrtc, "returning new");
1301 /* Any of the RTCIceTransports are in the checking or new state. */
1302 if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
1303 GST_TRACE_OBJECT (webrtc, "returning checking");
1304 return STATE (CHECKING);
1306 /* All RTCIceTransports are in the completed or closed state. */
1307 if (all_completed_or_closed) {
1308 GST_TRACE_OBJECT (webrtc, "returning completed");
1309 return STATE (COMPLETED);
1311 /* All RTCIceTransports are in the connected, completed or closed state. */
1312 if (all_connected_completed_or_closed) {
1313 GST_TRACE_OBJECT (webrtc, "returning connected");
1314 return STATE (CONNECTED);
1317 GST_FIXME ("unspecified situation, returning old state");
1318 return webrtc->ice_connection_state;
1322 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1323 static GstWebRTCICEGatheringState
1324 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1326 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1327 GstWebRTCICEGatheringState any_state = 0;
1328 GstWebRTCICEGatheringState ice_state;
1329 GstWebRTCDTLSTransport *dtls_transport;
1330 GstWebRTCICETransport *transport;
1331 gboolean all_completed = webrtc->priv->transceivers->len > 0 ||
1332 webrtc->priv->data_channel_transport;
1335 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1336 GstWebRTCRTPTransceiver *rtp_trans =
1337 g_ptr_array_index (webrtc->priv->transceivers, i);
1338 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1339 TransportStream *stream = trans->stream;
1341 if (rtp_trans->stopped || stream == NULL) {
1342 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1347 /* We only have a mid in the transceiver after we got the SDP answer,
1348 * which is usually long after gathering has finished */
1349 if (!rtp_trans->mid) {
1350 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1353 dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1354 if (dtls_transport == NULL) {
1355 GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1359 transport = dtls_transport->transport;
1361 /* get gathering state */
1362 g_object_get (transport, "gathering-state", &ice_state, NULL);
1363 GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1365 any_state |= (1 << ice_state);
1366 if (ice_state != STATE (COMPLETE))
1367 all_completed = FALSE;
1370 /* check data channel transport gathering state */
1371 if (all_completed && webrtc->priv->data_channel_transport) {
1372 if ((dtls_transport = webrtc->priv->data_channel_transport->transport)) {
1373 transport = dtls_transport->transport;
1374 g_object_get (transport, "gathering-state", &ice_state, NULL);
1375 GST_TRACE_OBJECT (webrtc,
1376 "data channel transport %p gathering state: 0x%x", dtls_transport,
1378 any_state |= (1 << ice_state);
1379 if (ice_state != STATE (COMPLETE))
1380 all_completed = FALSE;
1384 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1386 /* Any of the RTCIceTransport s are in the gathering state. */
1387 if (any_state & (1 << STATE (GATHERING))) {
1388 GST_TRACE_OBJECT (webrtc, "returning gathering");
1389 return STATE (GATHERING);
1391 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1392 * the completed gathering state. */
1393 if (all_completed) {
1394 GST_TRACE_OBJECT (webrtc, "returning complete");
1395 return STATE (COMPLETE);
1398 /* Any of the RTCIceTransport s are in the new gathering state and none
1399 * of the transports are in the gathering state, or there are no transports. */
1400 GST_TRACE_OBJECT (webrtc, "returning new");
1405 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1406 static GstWebRTCPeerConnectionState
1407 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1409 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1410 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1411 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1412 GstWebRTCICEConnectionState any_ice_state = 0;
1413 GstWebRTCDTLSTransportState any_dtls_state = 0;
1414 gboolean ice_all_new_or_closed = TRUE;
1415 gboolean dtls_all_new_or_closed = TRUE;
1416 gboolean ice_all_new_connecting_or_checking = TRUE;
1417 gboolean dtls_all_new_connecting_or_checking = TRUE;
1418 gboolean ice_all_connected_completed_or_closed = TRUE;
1419 gboolean dtls_all_connected_completed_or_closed = TRUE;
1422 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1423 GstWebRTCRTPTransceiver *rtp_trans =
1424 g_ptr_array_index (webrtc->priv->transceivers, i);
1425 GstWebRTCDTLSTransport *transport;
1426 GstWebRTCICEConnectionState ice_state;
1427 GstWebRTCDTLSTransportState dtls_state;
1429 if (rtp_trans->stopped) {
1430 GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1433 if (!rtp_trans->mid) {
1434 GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1438 transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1440 /* get transport state */
1441 g_object_get (transport, "state", &dtls_state, NULL);
1442 GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1444 any_dtls_state |= (1 << dtls_state);
1446 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1447 dtls_all_new_or_closed = FALSE;
1448 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1449 dtls_all_new_connecting_or_checking = FALSE;
1450 if (dtls_state != DTLS_STATE (CONNECTED)
1451 && dtls_state != DTLS_STATE (CLOSED))
1452 dtls_all_connected_completed_or_closed = FALSE;
1454 g_object_get (transport->transport, "state", &ice_state, NULL);
1455 GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1457 any_ice_state |= (1 << ice_state);
1459 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1460 ice_all_new_or_closed = FALSE;
1461 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1462 ice_all_new_connecting_or_checking = FALSE;
1463 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1464 && ice_state != ICE_STATE (CLOSED))
1465 ice_all_connected_completed_or_closed = FALSE;
1468 // also check data channel transport state
1469 if (webrtc->priv->data_channel_transport) {
1470 GstWebRTCDTLSTransport *transport =
1471 webrtc->priv->data_channel_transport->transport;
1472 GstWebRTCICEConnectionState ice_state;
1473 GstWebRTCDTLSTransportState dtls_state;
1475 g_object_get (transport, "state", &dtls_state, NULL);
1476 GST_TRACE_OBJECT (webrtc, "data channel transport DTLS state: 0x%x",
1478 any_dtls_state |= (1 << dtls_state);
1480 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1481 dtls_all_new_or_closed = FALSE;
1482 if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1483 dtls_all_new_connecting_or_checking = FALSE;
1484 if (dtls_state != DTLS_STATE (CONNECTED)
1485 && dtls_state != DTLS_STATE (CLOSED))
1486 dtls_all_connected_completed_or_closed = FALSE;
1488 g_object_get (transport->transport, "state", &ice_state, NULL);
1489 GST_TRACE_OBJECT (webrtc, "data channel transport ICE state: 0x%x",
1491 any_ice_state |= (1 << ice_state);
1493 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1494 ice_all_new_or_closed = FALSE;
1495 if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1496 ice_all_new_connecting_or_checking = FALSE;
1497 if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1498 && ice_state != ICE_STATE (CLOSED))
1499 ice_all_connected_completed_or_closed = FALSE;
1502 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1503 "state: 0x%x", any_ice_state, any_dtls_state);
1505 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
1506 if (webrtc->priv->is_closed) {
1507 GST_TRACE_OBJECT (webrtc, "returning closed");
1508 return STATE (CLOSED);
1511 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1512 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1513 GST_TRACE_OBJECT (webrtc, "returning failed");
1514 return STATE (FAILED);
1516 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1517 GST_TRACE_OBJECT (webrtc, "returning failed");
1518 return STATE (FAILED);
1521 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1523 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1524 GST_TRACE_OBJECT (webrtc, "returning disconnected");
1525 return STATE (DISCONNECTED);
1528 /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1529 * state, or there are no transports. */
1530 if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1531 || webrtc->priv->transports->len == 0) {
1532 GST_TRACE_OBJECT (webrtc, "returning new");
1536 /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1537 * or checking state. */
1538 if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1539 GST_TRACE_OBJECT (webrtc, "returning connecting");
1540 return STATE (CONNECTING);
1543 /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1544 * completed or closed state. */
1545 if (dtls_all_connected_completed_or_closed
1546 && ice_all_connected_completed_or_closed) {
1547 GST_TRACE_OBJECT (webrtc, "returning connected");
1548 return STATE (CONNECTED);
1551 /* FIXME: Unspecified state that happens for us */
1552 if ((dtls_all_new_connecting_or_checking
1553 || dtls_all_connected_completed_or_closed)
1554 && (ice_all_new_connecting_or_checking
1555 || ice_all_connected_completed_or_closed)) {
1556 GST_TRACE_OBJECT (webrtc, "returning connecting");
1557 return STATE (CONNECTING);
1560 GST_FIXME_OBJECT (webrtc,
1561 "Undefined situation detected, returning old state");
1562 return webrtc->peer_connection_state;
1568 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
1570 _update_and_notify_ice_gathering_state (GstWebRTCBin * webrtc, GstWebRTCICEGatheringState state)
1572 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1574 if (state != webrtc->ice_gathering_state) {
1575 const gchar *old_s, *new_s;
1577 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1579 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1581 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1582 old_s, old_state, new_s, state);
1584 webrtc->ice_gathering_state = state;
1586 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1592 static GstStructure *
1593 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1595 #ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
1596 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1598 GstWebRTCICEGatheringState new_state;
1600 new_state = _collate_ice_gathering_states (webrtc);
1602 /* If the new state is complete, before we update the public state,
1603 * check if anyone published more ICE candidates while we were collating
1604 * and stop if so, because it means there's a new later
1605 * ice_gathering_state_task queued */
1606 if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1608 if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1609 /* ICE candidates queued for emissiong -> we're gathering, not complete */
1610 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
1611 webrtc->pending_ice_gathering_state = GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE;
1612 GST_INFO_OBJECT (webrtc, "set pending_ice_gathering_state to (%u)",
1613 webrtc->pending_ice_gathering_state);
1614 ICE_UNLOCK (webrtc);
1618 new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1621 ICE_UNLOCK (webrtc);
1624 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
1625 _update_and_notify_ice_gathering_state (webrtc, new_state);
1627 if (new_state != webrtc->ice_gathering_state) {
1628 const gchar *old_s, *new_s;
1630 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1632 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1634 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1635 old_s, old_state, new_s, new_state);
1637 webrtc->ice_gathering_state = new_state;
1639 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1648 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1650 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1654 static GstStructure *
1655 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1657 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1658 GstWebRTCICEConnectionState new_state;
1660 new_state = _collate_ice_connection_states (webrtc);
1662 if (new_state != old_state) {
1663 const gchar *old_s, *new_s;
1665 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1667 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1669 GST_INFO_OBJECT (webrtc,
1670 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1673 webrtc->ice_connection_state = new_state;
1675 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1683 _update_ice_connection_state (GstWebRTCBin * webrtc)
1685 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1689 static GstStructure *
1690 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1692 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1693 GstWebRTCPeerConnectionState new_state;
1695 new_state = _collate_peer_connection_states (webrtc);
1697 if (new_state != old_state) {
1698 const gchar *old_s, *new_s;
1700 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1702 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1704 GST_INFO_OBJECT (webrtc,
1705 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1708 webrtc->peer_connection_state = new_state;
1710 g_object_notify (G_OBJECT (webrtc), "connection-state");
1718 _update_peer_connection_state (GstWebRTCBin * webrtc)
1720 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1725 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1728 gboolean res = FALSE;
1730 GST_OBJECT_LOCK (webrtc);
1731 l = GST_ELEMENT (webrtc)->pads;
1732 for (; l; l = g_list_next (l)) {
1733 GstWebRTCBinPad *wpad;
1735 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1738 wpad = GST_WEBRTC_BIN_PAD (l->data);
1739 if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1740 && (!wpad->trans || !wpad->trans->stopped)) {
1741 if (wpad->trans && wpad->trans->codec_preferences) {
1749 l = webrtc->priv->pending_pads;
1750 for (; l; l = g_list_next (l)) {
1751 if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1759 GST_OBJECT_UNLOCK (webrtc);
1763 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1765 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1769 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1771 /* We can't negotiate until we have received caps on all our sink pads,
1772 * as we will need the formats in our offer / answer */
1773 if (!_all_sinks_have_caps (webrtc)) {
1774 GST_LOG_OBJECT (webrtc,
1775 "no negotiation possible until caps have been received on all sink pads");
1779 /* If any implementation-specific negotiation is required, as described at
1780 * the start of this section, return "true".
1782 /* FIXME: emit when input caps/format changes? */
1784 if (!webrtc->current_local_description) {
1785 GST_LOG_OBJECT (webrtc, "no local description set");
1789 if (!webrtc->current_remote_description) {
1790 GST_LOG_OBJECT (webrtc, "no remote description set");
1794 /* If connection has created any RTCDataChannel's, and no m= section has
1795 * been negotiated yet for data, return "true". */
1796 if (webrtc->priv->data_channels->len > 0) {
1797 if (_message_get_datachannel_index (webrtc->current_local_description->
1798 sdp) >= G_MAXUINT) {
1799 GST_LOG_OBJECT (webrtc,
1800 "no data channel media section and have %u " "transports",
1801 webrtc->priv->data_channels->len);
1806 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1807 GstWebRTCRTPTransceiver *trans;
1809 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1811 if (trans->stopped) {
1812 /* FIXME: If t is stopped and is associated with an m= section according to
1813 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1814 * rejected in connection's currentLocalDescription or
1815 * currentRemoteDescription , return "true". */
1816 GST_FIXME_OBJECT (webrtc,
1817 "check if the transceiver is rejected in descriptions");
1819 const GstSDPMedia *media;
1820 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1822 if (trans->mline == -1 || trans->mid == NULL) {
1823 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1824 " mid %s", i, trans, trans->mid);
1827 /* internal inconsistency */
1828 g_assert (trans->mline <
1829 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1830 g_assert (trans->mline <
1831 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1833 /* FIXME: msid handling
1834 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1835 * section in connection's currentLocalDescription doesn't contain an
1836 * "a=msid" line, return "true". */
1839 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1841 local_dir = _get_direction_from_media (media);
1844 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1846 remote_dir = _get_direction_from_media (media);
1848 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1849 /* If connection's currentLocalDescription if of type "offer", and
1850 * the direction of the associated m= section in neither the offer
1851 * nor answer matches t's direction, return "true". */
1853 if (local_dir != trans->direction && remote_dir != trans->direction) {
1854 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1855 "description (local %s remote %s)",
1856 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction),
1857 gst_webrtc_rtp_transceiver_direction_to_string (local_dir),
1858 gst_webrtc_rtp_transceiver_direction_to_string (remote_dir));
1861 } else if (webrtc->current_local_description->type ==
1862 GST_WEBRTC_SDP_TYPE_ANSWER) {
1863 GstWebRTCRTPTransceiverDirection intersect_dir;
1865 /* If connection's currentLocalDescription if of type "answer", and
1866 * the direction of the associated m= section in the answer does not
1867 * match t's direction intersected with the offered direction (as
1868 * described in [JSEP] (section 5.3.1.)), return "true". */
1870 /* remote is the offer, local is the answer */
1871 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1873 if (intersect_dir != trans->direction) {
1874 GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1875 "description intersected direction %s (local %s remote %s)",
1876 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction),
1877 gst_webrtc_rtp_transceiver_direction_to_string (local_dir),
1878 gst_webrtc_rtp_transceiver_direction_to_string (intersect_dir),
1879 gst_webrtc_rtp_transceiver_direction_to_string (remote_dir));
1886 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1890 static GstStructure *
1891 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1893 if (webrtc->priv->need_negotiation) {
1894 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1896 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1904 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1906 _update_need_negotiation (GstWebRTCBin * webrtc)
1908 /* If connection's [[isClosed]] slot is true, abort these steps. */
1909 if (webrtc->priv->is_closed)
1911 /* If connection's signaling state is not "stable", abort these steps. */
1912 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1915 /* If the result of checking if negotiation is needed is "false", clear the
1916 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1917 * to false, and abort these steps. */
1918 if (!_check_if_negotiation_is_needed (webrtc)) {
1919 webrtc->priv->need_negotiation = FALSE;
1922 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1923 if (webrtc->priv->need_negotiation)
1925 /* Set connection's [[needNegotiation]] slot to true. */
1926 webrtc->priv->need_negotiation = TRUE;
1927 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1928 * true, fire a simple event named negotiationneeded at connection. */
1929 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1934 _query_pad_caps (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * rtp_trans,
1935 GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
1940 caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1941 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1943 /* Only return an error if actual empty caps were returned from the query. */
1944 if (gst_caps_is_empty (caps)) {
1945 g_set_error (error, GST_WEBRTC_ERROR,
1946 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
1947 "Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
1948 gst_clear_caps (&caps);
1949 gst_caps_unref (filter);
1953 n = gst_caps_get_size (caps);
1955 /* Make sure the caps are complete enough to figure out the media type and
1956 * encoding-name, otherwise they would match with basically any media. */
1957 caps = gst_caps_make_writable (caps);
1958 for (i = n; i > 0; i--) {
1959 const GstStructure *s = gst_caps_get_structure (caps, i - 1);
1961 if (!gst_structure_has_name (s, "application/x-rtp") ||
1962 !gst_structure_has_field (s, "media") ||
1963 !gst_structure_has_field (s, "encoding-name")) {
1964 gst_caps_remove_structure (caps, i - 1);
1969 /* If the filtering above resulted in empty caps, or the caps were ANY to
1970 * begin with, then don't report and error but just NULL.
1972 * This would be the case if negotiation would not fail but the peer does
1973 * not have any specific enough preferred caps that would allow us to
1976 if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
1977 GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
1978 gst_clear_caps (&caps);
1981 gst_caps_unref (filter);
1987 _find_codec_preferences (GstWebRTCBin * webrtc,
1988 GstWebRTCRTPTransceiver * rtp_trans, guint media_idx, GError ** error)
1990 WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1991 GstCaps *ret = NULL;
1992 GstCaps *codec_preferences = NULL;
1993 GstWebRTCBinPad *pad = NULL;
1994 GstPadDirection direction;
1996 g_assert (rtp_trans);
1997 g_assert (error && *error == NULL);
1999 GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
2002 GST_OBJECT_LOCK (rtp_trans);
2003 if (rtp_trans->codec_preferences) {
2004 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
2005 rtp_trans->codec_preferences);
2006 codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
2008 GST_OBJECT_UNLOCK (rtp_trans);
2010 if (rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
2011 direction = GST_PAD_SRC;
2013 direction = GST_PAD_SINK;
2015 pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
2017 /* try to find a pad */
2019 pad = _find_pad_for_mline (webrtc, direction, media_idx);
2021 /* For the case where we have set our transceiver to sendrecv, but the
2022 * sink pad has not been requested yet.
2025 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2027 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
2029 /* try to find a pad */
2031 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
2035 GstCaps *caps = NULL;
2037 if (pad->received_caps) {
2038 caps = gst_caps_ref (pad->received_caps);
2040 static GstStaticCaps static_filter =
2041 GST_STATIC_CAPS ("application/x-rtp, "
2042 "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
2043 GstCaps *filter = gst_static_caps_get (&static_filter);
2045 filter = gst_caps_make_writable (filter);
2047 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
2048 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
2049 else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
2050 gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
2052 caps = _query_pad_caps (webrtc, rtp_trans, pad, filter, error);
2059 rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2060 GstWebRTCBinPad *srcpad =
2061 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
2064 caps = _query_pad_caps (webrtc, rtp_trans, srcpad, caps, error);
2065 gst_object_unref (srcpad);
2072 if (caps && codec_preferences) {
2073 GstCaps *intersection;
2075 intersection = gst_caps_intersect_full (codec_preferences, caps,
2076 GST_CAPS_INTERSECT_FIRST);
2077 gst_clear_caps (&caps);
2079 if (gst_caps_is_empty (intersection)) {
2080 g_set_error (error, GST_WEBRTC_ERROR,
2081 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
2082 "Caps negotiation on pad %s failed against codec preferences",
2083 GST_PAD_NAME (pad));
2084 gst_clear_caps (&intersection);
2086 caps = intersection;
2091 #ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
2094 gst_caps_replace (&trans->last_retrieved_caps, caps);
2101 if (codec_preferences)
2102 ret = gst_caps_ref (codec_preferences);
2103 else if (trans->last_retrieved_caps)
2104 ret = gst_caps_ref (trans->last_retrieved_caps);
2110 gst_object_unref (pad);
2111 if (codec_preferences)
2112 gst_caps_unref (codec_preferences);
2115 GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
2121 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
2122 WebRTCTransceiver * trans, const GstCaps * caps)
2131 ret = gst_caps_make_writable (caps);
2133 kind = webrtc_kind_from_caps (ret);
2134 for (i = 0; i < gst_caps_get_size (ret); i++) {
2135 GstStructure *s = gst_caps_get_structure (ret, i);
2138 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
2139 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
2141 if (kind == GST_WEBRTC_KIND_VIDEO) {
2142 if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
2143 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
2144 if (!gst_structure_has_field (s, "rtcp-fb-ccm-fir"))
2145 gst_structure_set (s, "rtcp-fb-ccm-fir", G_TYPE_BOOLEAN, TRUE, NULL);
2147 if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
2148 gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
2150 /* FIXME: codec-specific parameters? */
2157 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
2158 GParamSpec * pspec, GstWebRTCBin * webrtc)
2160 _update_ice_connection_state (webrtc);
2161 _update_peer_connection_state (webrtc);
2165 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
2166 GParamSpec * pspec, GstWebRTCBin * webrtc)
2168 _update_ice_gathering_state (webrtc);
2172 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
2173 GParamSpec * pspec, GstWebRTCBin * webrtc)
2175 _update_peer_connection_state (webrtc);
2179 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
2180 gboolean early, gpointer user_data)
2182 GstWebRTCBin *webrtc = user_data;
2183 GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
2184 GstRTCPPacket packet;
2186 if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
2189 if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
2190 if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
2192 GstWebRTCRTPTransceiver *rtp_trans = NULL;
2193 WebRTCTransceiver *trans;
2197 gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
2200 GPOINTER_TO_UINT (g_object_get_data (internal_session,
2201 "GstWebRTCBinRTPSessionID"));
2203 mid = find_mid_ssrc_for_ssrc (webrtc,
2204 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session, ssrc);
2205 if (mid && mid->mid) {
2206 rtp_trans = _find_transceiver_for_mid (webrtc, mid->mid);
2207 GST_LOG_OBJECT (webrtc, "found %" GST_PTR_FORMAT " from mid entry "
2208 "using rtp session %u ssrc %u -> mid \'%s\'", rtp_trans,
2209 rtp_session, ssrc, mid->mid);
2211 trans = (WebRTCTransceiver *) rtp_trans;
2213 if (rtp_trans && rtp_trans->sender && trans->tos_event) {
2215 gchar *pad_name = NULL;
2218 g_strdup_printf ("send_rtcp_src_%u",
2219 rtp_trans->sender->transport->session_id);
2220 pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
2223 gst_pad_push_event (pad, gst_event_ref (trans->tos_event));
2224 gst_object_unref (pad);
2230 gst_rtcp_buffer_unmap (&rtcp);
2233 /* False means we don't care about suppression */
2238 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
2240 GObject *internal_session = NULL;
2242 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
2243 session_id, &internal_session);
2245 if (internal_session) {
2246 g_object_set_data (internal_session, "GstWebRTCBinRTPSessionID",
2247 GUINT_TO_POINTER (session_id));
2248 g_signal_connect (internal_session, "on-sending-rtcp",
2249 G_CALLBACK (_on_sending_rtcp), webrtc);
2250 g_object_unref (internal_session);
2255 weak_free (GWeakRef * weak)
2257 g_weak_ref_clear (weak);
2261 static GstPadProbeReturn
2262 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
2264 GstWebRTCBin *webrtc = g_weak_ref_get ((GWeakRef *) user_data);
2267 return GST_PAD_PROBE_REMOVE;
2269 if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
2270 == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
2271 const GstStructure *s =
2272 gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
2274 if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
2278 if ((mid = gst_structure_get_string (s, "mid"))) {
2279 GstWebRTCRTPTransceiver *rtp_trans;
2281 rtp_trans = _find_transceiver_for_mid (webrtc, mid);
2283 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
2284 GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
2285 trans->stream->session_id);
2288 /* Set DSCP field based on
2289 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2291 switch (rtp_trans->sender->priority) {
2292 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2295 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2298 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2299 switch (rtp_trans->kind) {
2300 case GST_WEBRTC_KIND_AUDIO:
2303 case GST_WEBRTC_KIND_VIDEO:
2304 dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
2306 case GST_WEBRTC_KIND_UNKNOWN:
2311 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2312 switch (rtp_trans->kind) {
2313 case GST_WEBRTC_KIND_AUDIO:
2316 case GST_WEBRTC_KIND_VIDEO:
2317 dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
2319 case GST_WEBRTC_KIND_UNKNOWN:
2326 gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
2328 } else if (gst_structure_get_enum (s, "sctp-priority",
2329 GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
2332 /* Set DSCP field based on
2333 * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2336 case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2339 case GST_WEBRTC_PRIORITY_TYPE_LOW:
2342 case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2343 dscp = 10; /* AF11 */
2345 case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2346 dscp = 18; /* AF21 */
2349 if (webrtc->priv->data_channel_transport)
2350 gst_webrtc_ice_set_tos (webrtc->priv->ice,
2351 webrtc->priv->data_channel_transport->stream, dscp << 2);
2356 gst_object_unref (webrtc);
2358 return GST_PAD_PROBE_OK;
2361 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
2364 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
2366 GstWebRTCPriorityType sctp_priority = 0;
2369 if (!webrtc->priv->sctp_transport)
2373 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2374 GstWebRTCDataChannel *channel
2375 = g_ptr_array_index (webrtc->priv->data_channels, i);
2377 sctp_priority = MAX (sctp_priority, channel->priority);
2381 /* Default priority is low means DSCP field is left as 0 */
2382 if (sctp_priority == 0)
2383 sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
2385 /* Nobody asks for DSCP, leave it as-is */
2386 if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
2387 !webrtc->priv->tos_attached)
2390 /* If one stream has a non-default priority, then everyone else does too */
2391 gst_webrtc_bin_attach_tos (webrtc);
2393 webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
2398 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
2399 GstWebRTCICETransport * transport)
2404 pad = gst_element_get_static_pad (transport->sink, "sink");
2406 weak = g_new0 (GWeakRef, 1);
2407 g_weak_ref_init (weak, webrtc);
2409 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2410 _nicesink_pad_probe, weak, (GDestroyNotify) weak_free);
2411 gst_object_unref (pad);
2415 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
2419 if (webrtc->priv->tos_attached)
2421 webrtc->priv->tos_attached = TRUE;
2423 for (i = 0; i < webrtc->priv->transports->len; i++) {
2424 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
2426 gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
2428 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
2429 stream->transport->transport);
2432 gst_webrtc_bin_update_sctp_priority (webrtc);
2435 static WebRTCTransceiver *
2436 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
2437 GstWebRTCRTPTransceiverDirection direction, guint mline, GstWebRTCKind kind,
2438 GstCaps * codec_preferences)
2440 WebRTCTransceiver *trans;
2441 GstWebRTCRTPTransceiver *rtp_trans;
2442 GstWebRTCRTPSender *sender;
2443 GstWebRTCRTPReceiver *receiver;
2445 sender = gst_webrtc_rtp_sender_new ();
2446 receiver = gst_webrtc_rtp_receiver_new ();
2447 trans = webrtc_transceiver_new (webrtc, sender, receiver);
2448 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2449 rtp_trans->direction = direction;
2450 rtp_trans->mline = mline;
2451 rtp_trans->kind = kind;
2452 rtp_trans->codec_preferences =
2453 codec_preferences ? gst_caps_ref (codec_preferences) : NULL;
2454 /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
2455 rtp_trans->stopped = FALSE;
2457 GST_LOG_OBJECT (webrtc, "created new transceiver %" GST_PTR_FORMAT " with "
2458 "direction %s (%d), mline %u, kind %s (%d)", rtp_trans,
2459 gst_webrtc_rtp_transceiver_direction_to_string (direction), direction,
2460 mline, gst_webrtc_kind_to_string (kind), kind);
2462 g_signal_connect_object (sender, "notify::priority",
2463 G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
2465 g_ptr_array_add (webrtc->priv->transceivers, trans);
2467 gst_object_unref (sender);
2468 gst_object_unref (receiver);
2470 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
2476 static TransportStream *
2477 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2479 GstWebRTCDTLSTransport *transport;
2480 TransportStream *ret;
2483 /* FIXME: how to parametrize the sender and the receiver */
2484 ret = transport_stream_new (webrtc, session_id);
2485 transport = ret->transport;
2487 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2488 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2489 g_signal_connect (G_OBJECT (transport->transport),
2490 "notify::gathering-state",
2491 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2492 g_signal_connect (G_OBJECT (transport), "notify::state",
2493 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2494 if (webrtc->priv->tos_attached)
2495 gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2497 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2498 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2499 g_ptr_array_add (webrtc->priv->transports, ret);
2501 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2502 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2503 GST_ELEMENT (webrtc->rtpbin), pad_name))
2504 g_warn_if_reached ();
2507 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2508 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2509 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2510 g_warn_if_reached ();
2513 GST_TRACE_OBJECT (webrtc,
2514 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2519 static TransportStream *
2520 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2522 TransportStream *ret;
2524 ret = _find_transport_for_session (webrtc, session_id);
2527 ret = _create_transport_channel (webrtc, session_id);
2529 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2530 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2535 /* this is called from the webrtc thread with the pc lock held */
2537 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2538 GParamSpec * pspec, GstWebRTCBin * webrtc)
2540 GstWebRTCDataChannelState ready_state;
2542 g_object_get (channel, "ready-state", &ready_state, NULL);
2544 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
2545 GST_TRACE_OBJECT (webrtc,
2546 "%" GST_PTR_FORMAT " ready-state %u", channel, ready_state);
2548 if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2552 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2553 if (found == FALSE) {
2554 GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2559 g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2560 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
2561 webrtc->priv->data_channels_opened++;
2565 gst_webrtc_bin_update_sctp_priority (webrtc);
2567 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2569 } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2573 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
2574 GST_WEBRTC_DATA_CHANNEL_LOCK (channel);
2575 if (channel->parent.prev_ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN ||
2576 channel->parent.prev_ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING)
2577 webrtc->priv->data_channels_closed++;
2578 GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel);
2580 found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2581 || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2583 if (found == FALSE) {
2584 GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2591 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2592 GstWebRTCBin * webrtc)
2594 WebRTCDataChannel *channel;
2598 if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2602 channel = _find_data_channel_for_id (webrtc, stream_id);
2604 channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2605 channel->parent.id = stream_id;
2606 webrtc_data_channel_set_webrtcbin (channel, webrtc);
2608 g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL],
2611 gst_bin_add (GST_BIN (webrtc), channel->src_bin);
2612 gst_bin_add (GST_BIN (webrtc), channel->sink_bin);
2614 gst_element_sync_state_with_parent (channel->src_bin);
2615 gst_element_sync_state_with_parent (channel->sink_bin);
2617 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2619 g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2623 g_signal_connect (channel, "notify::ready-state",
2624 G_CALLBACK (_on_data_channel_ready_state), webrtc);
2626 sink_pad = gst_element_get_static_pad (channel->sink_bin, "sink");
2627 if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2628 GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2629 GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2630 gst_object_unref (sink_pad);
2634 _on_sctp_state_notify (WebRTCSCTPTransport * sctp, GParamSpec * pspec,
2635 GstWebRTCBin * webrtc)
2637 GstWebRTCSCTPTransportState state;
2639 g_object_get (sctp, "state", &state, NULL);
2641 if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2644 GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2647 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2648 WebRTCDataChannel *channel;
2650 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2652 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2654 if (!channel->parent.negotiated && !channel->opened)
2655 webrtc_data_channel_start_negotiation (channel);
2661 /* Forward declaration so we can easily disconnect the signal handler */
2662 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2663 GParamSpec * pspec, GstWebRTCBin * webrtc);
2665 static GstStructure *
2666 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2668 TransportStream *stream;
2669 GstWebRTCDTLSTransport *transport;
2670 GstWebRTCDTLSTransportState dtls_state;
2671 WebRTCSCTPTransport *sctp_transport;
2673 stream = webrtc->priv->data_channel_transport;
2674 transport = stream->transport;
2676 g_object_get (transport, "state", &dtls_state, NULL);
2677 /* Not connected yet so just return */
2678 if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2679 GST_DEBUG_OBJECT (webrtc,
2680 "Data channel DTLS connection is not ready yet: %d", dtls_state);
2684 GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2685 sctp_transport = webrtc->priv->sctp_transport;
2687 /* Not locked state anymore so this was already taken care of before */
2688 if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2691 /* Start up the SCTP elements now that the DTLS connection is established */
2692 gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2693 gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2695 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2696 gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2698 if (sctp_transport->sctpdec_block_id) {
2699 GstPad *receive_srcpad;
2702 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2704 gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2706 sctp_transport->sctpdec_block_id = 0;
2707 gst_object_unref (receive_srcpad);
2710 g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2717 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2718 GParamSpec * pspec, GstWebRTCBin * webrtc)
2720 GstWebRTCDTLSTransportState dtls_state;
2722 g_object_get (transport, "state", &dtls_state, NULL);
2724 GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2727 /* Connected now, so schedule a task to update the state of the SCTP
2729 if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2730 gst_webrtc_bin_enqueue_task (webrtc,
2731 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2735 static GstPadProbeReturn
2736 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2738 /* Drop all events: we don't care about them and don't want to block on
2739 * them. Sticky events would be forwarded again later once we unblock
2740 * and we don't want to forward them here already because that might
2741 * cause a spurious GST_FLOW_FLUSHING */
2742 if (GST_IS_EVENT (info->data))
2743 return GST_PAD_PROBE_DROP;
2745 /* But block on any actual data-flow so we don't accidentally send that
2746 * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2749 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2751 return GST_PAD_PROBE_OK;
2754 static TransportStream *
2755 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2757 if (!webrtc->priv->data_channel_transport) {
2758 TransportStream *stream;
2759 WebRTCSCTPTransport *sctp_transport;
2761 stream = _find_transport_for_session (webrtc, session_id);
2764 stream = _create_transport_channel (webrtc, session_id);
2766 webrtc->priv->data_channel_transport = stream;
2768 if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2769 sctp_transport = webrtc_sctp_transport_new ();
2770 sctp_transport->transport =
2771 g_object_ref (webrtc->priv->data_channel_transport->transport);
2772 sctp_transport->webrtcbin = webrtc;
2774 /* Don't automatically start SCTP elements as part of webrtcbin. We
2775 * need to delay this until the DTLS transport is fully connected! */
2776 gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2777 gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2779 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2780 gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2783 g_signal_connect (sctp_transport->sctpdec, "pad-added",
2784 G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2785 g_signal_connect (sctp_transport, "notify::state",
2786 G_CALLBACK (_on_sctp_state_notify), webrtc);
2788 if (sctp_transport->sctpdec_block_id == 0) {
2789 GstPad *receive_srcpad;
2791 gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2793 sctp_transport->sctpdec_block_id =
2794 gst_pad_add_probe (receive_srcpad,
2795 GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2796 (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2797 gst_object_unref (receive_srcpad);
2800 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2801 GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2802 g_warn_if_reached ();
2804 if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2805 GST_ELEMENT (stream->send_bin), "data_sink"))
2806 g_warn_if_reached ();
2808 gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2809 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2811 if (!webrtc->priv->sctp_transport) {
2812 /* Connect to the notify::state signal to get notified when the DTLS
2813 * connection is established. Only then can we start the SCTP elements */
2814 g_signal_connect (stream->transport, "notify::state",
2815 G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2817 /* As this would be racy otherwise, also schedule a task that checks the
2818 * current state of the connection already without getting the signal
2820 gst_webrtc_bin_enqueue_task (webrtc,
2821 (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2824 webrtc->priv->sctp_transport = sctp_transport;
2826 gst_webrtc_bin_update_sctp_priority (webrtc);
2829 return webrtc->priv->data_channel_transport;
2832 static TransportStream *
2833 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2834 gboolean is_datachannel)
2837 return _get_or_create_data_channel_transports (webrtc, session_id);
2839 return _get_or_create_rtp_transport_channel (webrtc, session_id);
2842 struct media_payload_map_item
2852 media_payload_map_item_init (struct media_payload_map_item *item,
2855 item->media_pt = media_pt;
2856 item->red_pt = G_MAXUINT;
2857 item->rtx_pt = G_MAXUINT;
2858 item->ulpfec_pt = G_MAXUINT;
2859 item->red_rtx_pt = G_MAXUINT;
2862 static struct media_payload_map_item *
2863 find_payload_map_for_media_pt (GArray * media_mapping, guint media_pt)
2867 for (i = 0; i < media_mapping->len; i++) {
2868 struct media_payload_map_item *item;
2870 item = &g_array_index (media_mapping, struct media_payload_map_item, i);
2872 if (item->media_pt == media_pt)
2879 static struct media_payload_map_item *
2880 find_or_create_payload_map_for_media_pt (GArray * media_mapping, guint media_pt)
2882 struct media_payload_map_item new_item;
2883 struct media_payload_map_item *item;
2885 if ((item = find_payload_map_for_media_pt (media_mapping, media_pt)))
2888 media_payload_map_item_init (&new_item, media_pt);
2889 g_array_append_val (media_mapping, new_item);
2890 return &g_array_index (media_mapping, struct media_payload_map_item,
2891 media_mapping->len - 1);
2895 _pick_available_pt (GArray * media_mapping, guint * ret)
2899 for (i = 96; i <= 127; i++) {
2900 gboolean available = TRUE;
2903 for (j = 0; j < media_mapping->len; j++) {
2904 struct media_payload_map_item *item;
2906 item = &g_array_index (media_mapping, struct media_payload_map_item, j);
2908 if (item->media_pt == i || item->red_pt == i || item->rtx_pt == i
2909 || item->ulpfec_pt == i || item->red_rtx_pt == i) {
2926 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2927 GArray * media_mapping, gint clockrate, gint media_pt, gint * rtx_target_pt,
2928 GstSDPMedia * media)
2930 gboolean ret = TRUE;
2932 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2935 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2936 struct media_payload_map_item *item;
2939 item = find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
2940 if (item->red_pt == G_MAXUINT) {
2941 if (!(ret = _pick_available_pt (media_mapping, &item->red_pt)))
2945 /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2947 str = g_strdup_printf ("%u", item->red_pt);
2948 gst_sdp_media_add_format (media, str);
2950 str = g_strdup_printf ("%u red/%d", item->red_pt, clockrate);
2951 gst_sdp_media_add_attribute (media, "rtpmap", str);
2954 *rtx_target_pt = item->red_pt;
2956 if (item->ulpfec_pt == G_MAXUINT) {
2957 if (!(ret = _pick_available_pt (media_mapping, &item->ulpfec_pt)))
2961 str = g_strdup_printf ("%u", item->ulpfec_pt);
2962 gst_sdp_media_add_format (media, str);
2964 str = g_strdup_printf ("%u ulpfec/%d", item->ulpfec_pt, clockrate);
2965 gst_sdp_media_add_attribute (media, "rtpmap", str);
2974 add_rtx_to_media (WebRTCTransceiver * trans, gint clockrate, gint rtx_pt,
2975 gint rtx_target_pt, guint target_ssrc, GstSDPMedia * media)
2979 /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2980 if (target_ssrc != -1) {
2981 str = g_strdup_printf ("%u", target_ssrc);
2982 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2983 g_random_int (), NULL);
2987 str = g_strdup_printf ("%u", rtx_pt);
2988 gst_sdp_media_add_format (media, str);
2991 str = g_strdup_printf ("%u rtx/%d", rtx_pt, clockrate);
2992 gst_sdp_media_add_attribute (media, "rtpmap", str);
2995 str = g_strdup_printf ("%u apt=%d", rtx_pt, rtx_target_pt);
2996 gst_sdp_media_add_attribute (media, "fmtp", str);
3001 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
3002 GArray * media_mapping, gint clockrate, gint media_pt, gint target_pt,
3003 guint target_ssrc, GstSDPMedia * media)
3005 gboolean ret = TRUE;
3007 if (trans->local_rtx_ssrc_map)
3008 gst_structure_free (trans->local_rtx_ssrc_map);
3010 trans->local_rtx_ssrc_map =
3011 gst_structure_new_empty ("application/x-rtp-ssrc-map");
3013 if (trans->do_nack) {
3014 struct media_payload_map_item *item;
3016 item = find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
3017 if (item->rtx_pt == G_MAXUINT) {
3018 if (!(ret = _pick_available_pt (media_mapping, &item->rtx_pt)))
3022 add_rtx_to_media (trans, clockrate, item->rtx_pt, media_pt, target_ssrc,
3025 if (item->red_pt != G_MAXUINT) {
3026 /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
3027 if (item->red_rtx_pt == G_MAXUINT) {
3028 if (!(ret = _pick_available_pt (media_mapping, &item->red_rtx_pt)))
3031 add_rtx_to_media (trans, clockrate, item->red_rtx_pt, item->red_pt,
3032 target_ssrc, media);
3040 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
3042 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
3043 GstSDPMedia * media)
3048 g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
3049 g_value_get_uint (value));
3050 gst_sdp_media_add_attribute (media, "ssrc-group", str);
3060 GstWebRTCBin *webrtc;
3061 WebRTCTransceiver *trans;
3065 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
3070 GstWebRTCBinPad *sink_pad;
3071 const char *msid = NULL;
3073 g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
3074 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
3075 cname = gst_structure_get_string (sdes, "cname");
3078 _find_pad_for_transceiver (data->webrtc, GST_PAD_SINK,
3079 GST_WEBRTC_RTP_TRANSCEIVER (data->trans));
3081 msid = sink_pad->msid;
3082 /* fallback to cname if no msid provided */
3086 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
3087 /* FIXME: the ssrc is not present in RFC8830, do we still need that? */
3089 g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
3090 msid, GST_OBJECT_NAME (data->trans));
3091 gst_sdp_media_add_attribute (data->media, "ssrc", str);
3094 str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
3095 gst_sdp_media_add_attribute (data->media, "ssrc", str);
3098 gst_clear_object (&sink_pad);
3099 gst_structure_free (sdes);
3105 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
3106 WebRTCTransceiver * trans)
3109 RtxSsrcData data = { media, webrtc, trans };
3113 g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
3114 /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
3115 cname = gst_structure_get_string (sdes, "cname");
3117 if (trans->local_rtx_ssrc_map)
3118 gst_structure_foreach (trans->local_rtx_ssrc_map,
3119 (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
3121 for (i = 0; i < gst_caps_get_size (caps); i++) {
3122 const GstStructure *s = gst_caps_get_structure (caps, i);
3125 if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
3127 GstWebRTCBinPad *sink_pad;
3128 const char *msid = NULL;
3131 _find_pad_for_transceiver (webrtc, GST_PAD_SINK,
3132 GST_WEBRTC_RTP_TRANSCEIVER (trans));
3134 msid = sink_pad->msid;
3135 /* fallback to cname if no msid provided */
3139 /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
3140 /* FIXME: the ssrc is not present in RFC8830, do we still need that? */
3142 g_strdup_printf ("%u msid:%s %s", ssrc, msid,
3143 GST_OBJECT_NAME (trans));
3144 gst_sdp_media_add_attribute (media, "ssrc", str);
3147 str = g_strdup_printf ("%u cname:%s", ssrc, cname);
3148 gst_sdp_media_add_attribute (media, "ssrc", str);
3151 gst_clear_object (&sink_pad);
3155 gst_structure_free (sdes);
3157 if (trans->local_rtx_ssrc_map)
3158 gst_structure_foreach (trans->local_rtx_ssrc_map,
3159 (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
3163 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
3164 GstSDPMedia * media)
3166 gchar *cert, *fingerprint, *val;
3168 g_object_get (transport, "certificate", &cert, NULL);
3171 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
3174 g_strdup_printf ("%s %s",
3175 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
3176 g_free (fingerprint);
3178 gst_sdp_media_add_attribute (media, "fingerprint", val);
3183 _parse_extmap (GQuark field_id, const GValue * value, GError ** error)
3187 if (G_VALUE_HOLDS_STRING (value)) {
3188 ret = g_value_dup_string (value);
3189 } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
3190 && gst_value_array_get_size (value) == 3) {
3192 const gchar *direction, *extensionname, *extensionattributes;
3194 val = gst_value_array_get_value (value, 0);
3195 direction = g_value_get_string (val);
3197 val = gst_value_array_get_value (value, 1);
3198 extensionname = g_value_get_string (val);
3200 val = gst_value_array_get_value (value, 2);
3201 extensionattributes = g_value_get_string (val);
3203 if (!extensionname || *extensionname == '\0')
3206 if (direction && *direction != '\0' && extensionattributes
3207 && *extensionattributes != '\0') {
3209 g_strdup_printf ("/%s %s %s", direction, extensionname,
3210 extensionattributes);
3211 } else if (direction && *direction != '\0') {
3212 ret = g_strdup_printf ("/%s %s", direction, extensionname);
3213 } else if (extensionattributes && *extensionattributes != '\0') {
3214 ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
3216 ret = g_strdup (extensionname);
3220 if (!ret && error) {
3221 gchar *val_str = gst_value_serialize (value);
3223 g_set_error (error, GST_WEBRTC_ERROR,
3224 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3225 "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
3236 GstStructure *extmap;
3241 _dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
3243 gboolean is_extmap =
3244 g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
3250 gchar *new_value = _parse_extmap (field_id, value, data->error);
3257 if (gst_structure_id_has_field (data->extmap, field_id)) {
3259 _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
3262 g_assert (old_value);
3264 if (g_strcmp0 (new_value, old_value)) {
3266 ("extmap contains different values for id %s (%s != %s)",
3267 g_quark_to_string (field_id), old_value, new_value);
3268 g_set_error (data->error, GST_WEBRTC_ERROR,
3269 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3270 "extmap contains different values for id %s (%s != %s)",
3271 g_quark_to_string (field_id), old_value, new_value);
3280 gst_structure_id_set_value (data->extmap, field_id, value);
3290 static GstStructure *
3291 _gather_extmap (GstCaps * caps, GError ** error)
3294 { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
3297 n = gst_caps_get_size (caps);
3299 for (i = 0; i < n; i++) {
3300 GstStructure *s = gst_caps_get_structure (caps, i);
3302 gst_structure_filter_and_map_in_place (s,
3303 (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
3306 gst_clear_structure (&edata.extmap);
3311 return edata.extmap;
3316 const char *rtphdrext_uri;
3321 structure_value_get_rtphdrext_id (GQuark field_id, const GValue * value,
3324 struct hdrext_id *rtphdrext = user_data;
3325 const char *field_name = g_quark_to_string (field_id);
3327 if (g_str_has_prefix (field_name, "extmap-")) {
3328 const char *val = NULL;
3330 if (GST_VALUE_HOLDS_ARRAY (value) && gst_value_array_get_size (value) >= 2) {
3331 value = gst_value_array_get_value (value, 1);
3333 if (G_VALUE_HOLDS_STRING (value)) {
3334 val = g_value_get_string (value);
3337 if (g_strcmp0 (val, rtphdrext->rtphdrext_uri) == 0) {
3338 gint64 id = g_ascii_strtoll (&field_name[strlen ("extmap-")], NULL, 10);
3340 if (id > 0 && id < 256)
3341 rtphdrext->ext_id = id;
3350 // Returns -1 when not found
3352 caps_get_rtp_header_extension_id (const GstCaps * caps,
3353 const char *rtphdrext_uri)
3357 n = gst_caps_get_size (caps);
3358 for (i = 0; i < n; i++) {
3359 const GstStructure *s = gst_caps_get_structure (caps, i);
3360 struct hdrext_id data = { rtphdrext_uri, -1 };
3362 gst_structure_foreach (s, structure_value_get_rtphdrext_id, &data);
3364 if (data.ext_id != -1)
3372 caps_contain_rtp_header_extension (const GstCaps * caps,
3373 const char *rtphdrext_uri)
3375 return caps_get_rtp_header_extension_id (caps, rtphdrext_uri) != -1;
3379 _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
3381 gst_structure_id_set_value (s, field_id, value);
3386 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
3388 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
3389 const GstSDPMedia * last_media, GstWebRTCRTPTransceiver * trans,
3390 guint media_idx, GString * bundled_mids, guint bundle_idx,
3391 gchar * bundle_ufrag, gchar * bundle_pwd, GArray * media_mapping,
3392 GHashTable * all_mids, gboolean * no_more_mlines, GError ** error)
3395 * rtp header extensions
3402 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
3404 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3405 gchar *ufrag, *pwd, *mid = NULL;
3406 gboolean bundle_only;
3407 guint rtp_session_idx;
3409 GstStructure *extmap;
3412 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3415 g_assert (trans->mline == -1 || trans->mline == media_idx);
3417 rtp_session_idx = bundled_mids ? bundle_idx : media_idx;
3419 bundle_only = bundled_mids && bundle_idx != media_idx
3420 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
3422 caps = _find_codec_preferences (webrtc, trans, media_idx, error);
3423 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3426 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
3427 gst_clear_caps (&caps);
3432 n = gst_sdp_media_formats_len (last_media);
3434 caps = gst_caps_new_empty ();
3435 for (i = 0; i < n; i++) {
3436 guint fmt = atoi (gst_sdp_media_get_format (last_media, i));
3437 GstCaps *tmp = gst_sdp_media_get_caps_from_media (last_media, fmt);
3438 GstStructure *s = gst_caps_get_structure (tmp, 0);
3439 gst_structure_set_name (s, "application/x-rtp");
3440 gst_caps_append_structure (caps, gst_structure_copy (s));
3441 gst_clear_caps (&tmp);
3443 GST_DEBUG_OBJECT (webrtc, "using previously negotiated caps for "
3444 "transceiver %" GST_PTR_FORMAT " %" GST_PTR_FORMAT, trans, caps);
3449 if (WEBRTC_TRANSCEIVER (trans)->mline_locked) {
3450 GST_WARNING_OBJECT (webrtc,
3451 "Transceiver <%s> with mid %s has locked mline %u, but no caps. "
3452 "Can't add more lines after this one.", GST_OBJECT_NAME (trans),
3453 trans->mid, trans->mline);
3454 *no_more_mlines = TRUE;
3456 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver %"
3457 GST_PTR_FORMAT ", skipping", trans);
3464 const char *setup = gst_sdp_media_get_attribute_val (last_media, "setup");
3466 gst_sdp_media_add_attribute (media, "setup", setup);
3468 g_set_error (error, GST_WEBRTC_ERROR,
3469 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3470 "media %u cannot renegotiate without an existing a=setup line",
3475 /* mandated by JSEP */
3476 gst_sdp_media_add_attribute (media, "setup", "actpass");
3479 /* FIXME: deal with ICE restarts */
3480 if (last_offer && trans->mline != -1 && trans->mid) {
3481 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
3482 pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
3483 GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
3485 GST_DEBUG_OBJECT (trans,
3486 "%u Generating new ice parameters mline %i, mid %s", media_idx,
3487 trans->mline, trans->mid);
3488 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3489 _generate_ice_credentials (&ufrag, &pwd);
3491 g_assert (bundle_ufrag && bundle_pwd);
3492 ufrag = g_strdup (bundle_ufrag);
3493 pwd = g_strdup (bundle_pwd);
3497 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3498 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3502 gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
3503 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3504 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3507 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3510 /* FIXME: negotiate this */
3511 /* FIXME: when bundle_only, these should not be added:
3512 * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
3513 * However, this causes incompatibilities with current versions
3514 * of the major browsers */
3515 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
3516 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
3518 gst_sdp_media_add_attribute (media,
3519 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction), "");
3521 caps = gst_caps_make_writable (caps);
3523 /* When an extmap is defined twice for the same ID, firefox complains and
3524 * errors out (chrome is smart enough to accept strict duplicates).
3526 * To work around this, we deduplicate extmap attributes, and also error
3527 * out when a different extmap is defined for the same ID.
3529 * _gather_extmap will strip out all extmap- fields, which will then be
3530 * added upon adding the first format for the media.
3532 extmap = _gather_extmap (caps, error);
3535 GST_ERROR_OBJECT (webrtc,
3536 "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
3537 gst_caps_unref (caps);
3541 caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3544 for (i = 0; i < gst_caps_get_size (caps); i++) {
3545 GstCaps *format = gst_caps_new_empty ();
3546 GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
3549 gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
3552 gst_caps_append_structure (format, s);
3554 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
3555 " to %u-th media", i, format, media_idx);
3557 /* this only looks at the first structure so we loop over the given caps
3558 * and add each structure inside it piecemeal */
3559 if (gst_sdp_media_set_media_from_caps (format, media) != GST_SDP_OK) {
3560 GST_ERROR_OBJECT (webrtc,
3561 "Failed to build media from caps %" GST_PTR_FORMAT
3562 " for transceiver %" GST_PTR_FORMAT, format, trans);
3563 gst_caps_unref (caps);
3564 gst_caps_unref (format);
3565 gst_structure_free (extmap);
3569 gst_caps_unref (format);
3572 gst_clear_structure (&extmap);
3575 const GstStructure *s = gst_caps_get_structure (caps, 0);
3576 gint clockrate = -1;
3578 guint rtx_target_ssrc = -1;
3581 if (gst_structure_get_int (s, "payload", &media_pt) &&
3582 webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3583 find_or_create_payload_map_for_media_pt (media_mapping, media_pt);
3585 rtx_target_pt = media_pt;
3587 if (!gst_structure_get_int (s, "clock-rate", &clockrate))
3588 GST_WARNING_OBJECT (webrtc,
3589 "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
3590 if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc)) {
3591 if (!caps_contain_rtp_header_extension (caps, RTPHDREXT_MID)) {
3592 GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
3597 _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), media_mapping,
3598 clockrate, media_pt, &rtx_target_pt, media);
3599 _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), media_mapping,
3600 clockrate, media_pt, rtx_target_pt, rtx_target_ssrc, media);
3603 _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
3605 /* Some identifier; we also add the media name to it so it's identifiable */
3607 const char *media_mid = gst_sdp_media_get_attribute_val (media, "mid");
3610 gst_sdp_media_add_attribute (media, "mid", trans->mid);
3611 } else if (g_strcmp0 (media_mid, trans->mid) != 0) {
3612 g_set_error (error, GST_WEBRTC_ERROR,
3613 GST_WEBRTC_ERROR_INVALID_MODIFICATION,
3614 "Cannot change media %u mid value from \'%s\' to \'%s\'",
3615 media_idx, media_mid, trans->mid);
3618 mid = g_strdup (trans->mid);
3619 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3623 const GstStructure *s = gst_caps_get_structure (caps, 0);
3625 mid = g_strdup (gst_structure_get_string (s, "a-mid"));
3627 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3628 g_set_error (error, GST_WEBRTC_ERROR,
3629 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3630 "Cannot re-use mid \'%s\' from the caps in m= line %u that has "
3631 "already been used for a previous m= line in the SDP", mid,
3635 g_free (WEBRTC_TRANSCEIVER (trans)->pending_mid);
3636 WEBRTC_TRANSCEIVER (trans)->pending_mid = g_strdup (mid);
3637 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3642 mid = g_strdup (WEBRTC_TRANSCEIVER (trans)->pending_mid);
3644 /* If it's already used, just ignore the pending one and generate
3646 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3647 g_clear_pointer (&mid, free);
3648 g_clear_pointer (&WEBRTC_TRANSCEIVER (trans)->pending_mid, free);
3650 gst_sdp_media_add_attribute (media, "mid", mid);
3651 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3657 /* Make sure to avoid mid collisions */
3659 mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3660 webrtc->priv->media_counter++);
3661 if (g_hash_table_contains (all_mids, (gpointer) mid)) {
3664 gst_sdp_media_add_attribute (media, "mid", mid);
3665 g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3666 WEBRTC_TRANSCEIVER (trans)->pending_mid = g_strdup (mid);
3673 * - add a=candidate lines for gathered candidates
3676 if (trans->sender) {
3677 if (!trans->sender->transport) {
3678 TransportStream *item;
3680 item = _get_or_create_transport_stream (webrtc, rtp_session_idx, FALSE);
3682 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
3685 _add_fingerprint_to_media (trans->sender->transport, media);
3690 g_string_append_printf (bundled_mids, " %s", mid);
3693 g_clear_pointer (&mid, g_free);
3695 gst_caps_unref (caps);
3701 gather_pad_pt (GstWebRTCBinPad * pad, GArray * media_mapping)
3703 if (pad->received_caps) {
3704 GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
3707 if (gst_structure_get_int (s, "payload", &pt)) {
3708 GST_TRACE_OBJECT (pad, "have media pt %u from received caps", pt);
3709 find_or_create_payload_map_for_media_pt (media_mapping, pt);
3715 gather_media_mapping (GstWebRTCBin * webrtc)
3717 GstElement *element = GST_ELEMENT (webrtc);
3718 GArray *media_mapping =
3719 g_array_new (FALSE, FALSE, sizeof (struct media_payload_map_item));
3722 GST_OBJECT_LOCK (webrtc);
3723 g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, media_mapping);
3724 g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
3727 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3728 GstWebRTCRTPTransceiver *trans;
3730 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3731 GST_OBJECT_LOCK (trans);
3732 if (trans->codec_preferences) {
3736 n = gst_caps_get_size (trans->codec_preferences);
3737 for (j = 0; j < n; j++) {
3738 GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
3739 if (gst_structure_get_int (s, "payload", &pt)) {
3740 GST_TRACE_OBJECT (trans, "have media pt %u from codec preferences",
3742 find_or_create_payload_map_for_media_pt (media_mapping, pt);
3746 GST_OBJECT_UNLOCK (trans);
3748 GST_OBJECT_UNLOCK (webrtc);
3750 return media_mapping;
3754 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
3755 GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
3756 gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
3758 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3759 gchar *ufrag, *pwd, *sdp_mid;
3760 gboolean bundle_only = bundled_mids
3761 && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
3762 && gst_sdp_message_medias_len (msg) != bundle_idx;
3763 guint last_data_index = G_MAXUINT;
3765 /* add data channel support */
3766 if (webrtc->priv->data_channels->len == 0)
3770 last_data_index = _message_get_datachannel_index (last_offer);
3771 if (last_data_index < G_MAXUINT) {
3772 g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
3773 /* XXX: is this always true when recycling transceivers?
3774 * i.e. do we always put the data channel in the same mline */
3775 g_assert (last_data_index == gst_sdp_message_medias_len (msg));
3779 /* mandated by JSEP */
3780 gst_sdp_media_add_attribute (media, "setup", "actpass");
3782 /* FIXME: only needed when restarting ICE */
3783 if (last_offer && last_data_index < G_MAXUINT) {
3784 ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
3785 pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
3787 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3788 _generate_ice_credentials (&ufrag, &pwd);
3790 ufrag = g_strdup (bundle_ufrag);
3791 pwd = g_strdup (bundle_pwd);
3794 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3795 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3799 gst_sdp_media_set_media (media, "application");
3800 gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
3801 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3802 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3803 gst_sdp_media_add_format (media, "webrtc-datachannel");
3805 if (bundle_idx != gst_sdp_message_medias_len (msg))
3806 gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3808 if (last_offer && last_data_index < G_MAXUINT) {
3809 const GstSDPMedia *last_data_media;
3812 last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
3813 mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
3815 gst_sdp_media_add_attribute (media, "mid", mid);
3817 /* Make sure to avoid mid collisions */
3819 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3820 webrtc->priv->media_counter++);
3821 if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3824 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3825 g_hash_table_insert (all_mids, sdp_mid, NULL);
3832 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3835 g_string_append_printf (bundled_mids, " %s", mid);
3838 /* FIXME: negotiate this properly */
3839 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3841 _get_or_create_data_channel_transports (webrtc,
3842 bundled_mids ? 0 : webrtc->priv->transceivers->len);
3843 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
3848 /* TODO: use the options argument */
3849 static GstSDPMessage *
3850 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3853 GstSDPMessage *ret = NULL;
3854 GString *bundled_mids = NULL;
3855 gchar *bundle_ufrag = NULL;
3856 gchar *bundle_pwd = NULL;
3857 GArray *media_mapping = NULL;
3858 GHashTable *all_mids =
3859 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3861 GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3862 GList *seen_transceivers = NULL;
3863 guint media_idx = 0;
3865 gboolean no_more_mlines = FALSE;
3867 gst_sdp_message_new (&ret);
3869 gst_sdp_message_set_version (ret, "0");
3872 v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
3874 const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
3875 sess_id = g_strdup (origin->sess_id);
3877 sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
3879 gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
3883 gst_sdp_message_set_session_name (ret, "-");
3884 gst_sdp_message_add_time (ret, "0", "0", NULL);
3885 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
3887 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
3888 bundled_mids = g_string_new ("BUNDLE");
3889 } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
3890 bundled_mids = g_string_new ("BUNDLE");
3893 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3894 GStrv last_bundle = NULL;
3895 guint bundle_media_index;
3897 media_mapping = gather_media_mapping (webrtc);
3898 if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
3899 && last_bundle && last_bundle[0]
3900 && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
3902 g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
3904 g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
3906 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3909 g_strfreev (last_bundle);
3912 /* FIXME: recycle transceivers */
3914 /* Fill up the renegotiated streams first */
3916 for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
3917 GstWebRTCRTPTransceiver *trans = NULL;
3918 const GstSDPMedia *last_media;
3920 last_media = gst_sdp_message_get_media (last_offer, i);
3922 if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
3923 || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
3924 const gchar *last_mid;
3927 last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
3929 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3930 WebRTCTransceiver *wtrans;
3933 trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3934 wtrans = WEBRTC_TRANSCEIVER (trans);
3939 mid = wtrans->pending_mid;
3941 if (mid && g_strcmp0 (mid, last_mid) == 0) {
3944 memset (&media, 0, sizeof (media));
3946 g_assert (!g_list_find (seen_transceivers, trans));
3948 if (wtrans->mline_locked && trans->mline != media_idx) {
3949 g_set_error (error, GST_WEBRTC_ERROR,
3950 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3951 "Previous negotiatied transceiver <%s> with mid %s was in "
3952 "mline %d but transceiver has locked mline %u",
3953 GST_OBJECT_NAME (trans), trans->mid, media_idx, trans->mline);
3957 GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3958 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3959 trans->mid, media_idx);
3961 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3963 g_array_new (FALSE, FALSE,
3964 sizeof (struct media_payload_map_item));
3967 gst_sdp_media_init (&media);
3968 if (!sdp_media_from_transceiver (webrtc, &media, last_media, trans,
3969 media_idx, bundled_mids, 0, bundle_ufrag, bundle_pwd,
3970 media_mapping, all_mids, &no_more_mlines, error)) {
3971 gst_sdp_media_uninit (&media);
3973 g_set_error_literal (error, GST_WEBRTC_ERROR,
3974 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
3975 "Could not reuse transceiver");
3978 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3979 g_array_free (media_mapping, TRUE);
3980 media_mapping = NULL;
3985 mid = gst_sdp_media_get_attribute_val (&media, "mid");
3986 g_assert (mid && g_strcmp0 (last_mid, mid) == 0);
3988 gst_sdp_message_add_media (ret, &media);
3991 gst_sdp_media_uninit (&media);
3992 seen_transceivers = g_list_prepend (seen_transceivers, trans);
3996 } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3997 "application") == 0) {
3998 GstSDPMedia media = { 0, };
3999 gst_sdp_media_init (&media);
4000 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
4001 bundle_ufrag, bundle_pwd, all_mids)) {
4002 gst_sdp_message_add_media (ret, &media);
4005 gst_sdp_media_uninit (&media);
4011 /* First, go over all transceivers and gather existing mids */
4012 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4013 GstWebRTCRTPTransceiver *trans;
4015 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
4017 if (g_list_find (seen_transceivers, trans))
4021 if (g_hash_table_contains (all_mids, trans->mid)) {
4022 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4023 "Duplicate mid %s when creating offer", trans->mid);
4027 g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
4028 } else if (WEBRTC_TRANSCEIVER (trans)->pending_mid &&
4029 !g_hash_table_contains (all_mids,
4030 WEBRTC_TRANSCEIVER (trans)->pending_mid)) {
4031 g_hash_table_insert (all_mids,
4032 g_strdup (WEBRTC_TRANSCEIVER (trans)->pending_mid), NULL);
4037 /* add any extra streams */
4039 GstWebRTCRTPTransceiver *trans = NULL;
4040 GstSDPMedia media = { 0, };
4042 /* First find a transceiver requesting this m-line */
4043 trans = _find_transceiver_for_mline (webrtc, media_idx);
4046 /* We can't have seen it already, because it is locked to this line,
4047 * unless it's a no-more-mlines case
4049 if (!g_list_find (seen_transceivers, trans))
4050 seen_transceivers = g_list_prepend (seen_transceivers, trans);
4052 /* Otherwise find a free transceiver */
4053 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4054 WebRTCTransceiver *wtrans;
4056 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
4057 wtrans = WEBRTC_TRANSCEIVER (trans);
4059 /* don't add transceivers twice */
4060 if (g_list_find (seen_transceivers, trans))
4063 /* Ignore transceivers with a locked mline, as they would have been
4064 * found above or will be used later */
4065 if (wtrans->mline_locked)
4068 seen_transceivers = g_list_prepend (seen_transceivers, trans);
4069 /* don't add stopped transceivers */
4070 if (trans->stopped) {
4074 /* Otherwise take it */
4078 /* Stop if we got all transceivers */
4079 if (i == webrtc->priv->transceivers->len) {
4081 /* But try to add a data channel first, we do it here, because
4082 * it can allow a locked m-line to be put after, so we need to
4083 * do another iteration after.
4085 if (_message_get_datachannel_index (ret) == G_MAXUINT) {
4086 GstSDPMedia media = { 0, };
4087 gst_sdp_media_init (&media);
4088 if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
4089 bundle_ufrag, bundle_pwd, all_mids)) {
4090 if (no_more_mlines) {
4091 g_set_error (error, GST_WEBRTC_ERROR,
4092 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4093 "Trying to add data channel but there is a"
4094 " transceiver locked to line %d which doesn't have caps",
4096 gst_sdp_media_uninit (&media);
4099 gst_sdp_message_add_media (ret, &media);
4103 gst_sdp_media_uninit (&media);
4107 /* Verify that we didn't ignore any locked m-line transceivers */
4108 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4109 WebRTCTransceiver *wtrans;
4111 trans = g_ptr_array_index (webrtc->priv->transceivers, i);
4112 wtrans = WEBRTC_TRANSCEIVER (trans);
4113 /* don't add transceivers twice */
4114 if (g_list_find (seen_transceivers, trans))
4116 g_assert (wtrans->mline_locked);
4118 g_set_error (error, GST_WEBRTC_ERROR,
4119 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4120 "Tranceiver <%s> with mid %s has locked mline %d but the offer "
4121 "only has %u sections", GST_OBJECT_NAME (trans), trans->mid,
4122 trans->mline, media_idx);
4129 if (no_more_mlines) {
4130 g_set_error (error, GST_WEBRTC_ERROR,
4131 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
4132 "Trying to add transceiver at line %u but there is a transceiver "
4133 "with a locked mline for this line which doesn't have caps",
4138 gst_sdp_media_init (&media);
4140 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
4142 g_array_new (FALSE, FALSE, sizeof (struct media_payload_map_item));
4145 GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
4146 "index %u", trans, media_idx);
4148 if (sdp_media_from_transceiver (webrtc, &media, NULL, trans, media_idx,
4149 bundled_mids, 0, bundle_ufrag, bundle_pwd, media_mapping, all_mids,
4150 &no_more_mlines, error)) {
4151 /* as per JSEP, a=rtcp-mux-only is only added for new streams */
4152 gst_sdp_media_add_attribute (&media, "rtcp-mux-only", "");
4153 gst_sdp_message_add_media (ret, &media);
4156 gst_sdp_media_uninit (&media);
4159 if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
4160 g_array_free (media_mapping, TRUE);
4161 media_mapping = NULL;
4167 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
4168 g_array_free (media_mapping, TRUE);
4169 media_mapping = NULL;
4172 webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
4175 g_assert (media_idx == gst_sdp_message_medias_len (ret));
4178 gchar *mids = g_string_free (bundled_mids, FALSE);
4180 gst_sdp_message_add_attribute (ret, "group", mids);
4182 bundled_mids = NULL;
4185 /* FIXME: pre-emptively setup receiving elements when needed */
4187 if (webrtc->priv->last_generated_answer)
4188 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4189 webrtc->priv->last_generated_answer = NULL;
4190 if (webrtc->priv->last_generated_offer)
4191 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4193 GstSDPMessage *copy;
4194 gst_sdp_message_copy (ret, ©);
4195 webrtc->priv->last_generated_offer =
4196 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
4201 g_array_free (media_mapping, TRUE);
4203 g_hash_table_unref (all_mids);
4205 g_list_free (seen_transceivers);
4208 g_free (bundle_ufrag);
4211 g_free (bundle_pwd);
4214 g_string_free (bundled_mids, TRUE);
4219 gst_sdp_message_free (ret);
4225 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
4226 gint * rtx_target_pt)
4230 if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
4233 for (i = 0; i < gst_caps_get_size (caps); i++) {
4234 const GstStructure *s = gst_caps_get_structure (caps, i);
4236 if (gst_structure_has_name (s, "application/x-rtp")) {
4237 const gchar *encoding_name =
4238 gst_structure_get_string (s, "encoding-name");
4242 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
4243 gst_structure_get_int (s, "payload", &pt)) {
4244 if (!g_strcmp0 (encoding_name, "RED")) {
4247 str = g_strdup_printf ("%u", pt);
4248 gst_sdp_media_add_format (media, str);
4250 str = g_strdup_printf ("%u red/%d", pt, clock_rate);
4251 *rtx_target_pt = pt;
4252 gst_sdp_media_add_attribute (media, "rtpmap", str);
4254 } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
4257 str = g_strdup_printf ("%u", pt);
4258 gst_sdp_media_add_format (media, str);
4260 str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
4261 gst_sdp_media_add_attribute (media, "rtpmap", str);
4270 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
4271 GstCaps * offer_caps, gint target_pt, guint target_ssrc)
4274 const GstStructure *s;
4276 if (trans->local_rtx_ssrc_map)
4277 gst_structure_free (trans->local_rtx_ssrc_map);
4279 trans->local_rtx_ssrc_map =
4280 gst_structure_new_empty ("application/x-rtp-ssrc-map");
4282 for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
4283 s = gst_caps_get_structure (offer_caps, i);
4285 if (gst_structure_has_name (s, "application/x-rtp")) {
4286 const gchar *encoding_name =
4287 gst_structure_get_string (s, "encoding-name");
4288 const gchar *apt_str = gst_structure_get_string (s, "apt");
4296 apt = atoi (apt_str);
4298 if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
4299 gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
4300 if (!g_strcmp0 (encoding_name, "RTX")) {
4303 str = g_strdup_printf ("%u", pt);
4304 gst_sdp_media_add_format (media, str);
4306 str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
4307 gst_sdp_media_add_attribute (media, "rtpmap", str);
4310 str = g_strdup_printf ("%d apt=%d", pt, apt);
4311 gst_sdp_media_add_attribute (media, "fmtp", str);
4314 str = g_strdup_printf ("%u", target_ssrc);
4315 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
4316 g_random_int (), NULL);
4325 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
4326 const GstCaps * caps)
4328 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
4330 if (trans->kind == kind)
4333 if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
4342 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
4343 guint * target_ssrc)
4345 const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
4347 gst_structure_get_int (s, "payload", target_pt);
4348 gst_structure_get_uint (s, "ssrc", target_ssrc);
4351 /* TODO: use the options argument */
4352 static GstSDPMessage *
4353 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
4356 GstSDPMessage *ret = NULL;
4357 const GstWebRTCSessionDescription *pending_remote =
4358 webrtc->pending_remote_description;
4360 GStrv bundled = NULL;
4361 guint bundle_idx = 0;
4362 GString *bundled_mids = NULL;
4363 gchar *bundle_ufrag = NULL;
4364 gchar *bundle_pwd = NULL;
4365 GList *seen_transceivers = NULL;
4366 GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
4368 if (!webrtc->pending_remote_description) {
4369 g_set_error_literal (error, GST_WEBRTC_ERROR,
4370 GST_WEBRTC_ERROR_INVALID_STATE,
4371 "Asked to create an answer without a remote description");
4375 if (!_parse_bundle (pending_remote->sdp, &bundled, error))
4379 GStrv last_bundle = NULL;
4380 guint bundle_media_index;
4382 if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
4383 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
4384 "Bundle tag is %s but no media found matching", bundled[0]);
4388 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
4389 bundled_mids = g_string_new ("BUNDLE");
4392 if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
4393 && last_bundle && last_bundle[0]
4394 && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
4396 g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
4398 g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
4400 _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
4403 g_strfreev (last_bundle);
4406 gst_sdp_message_new (&ret);
4408 gst_sdp_message_set_version (ret, "0");
4410 const GstSDPOrigin *offer_origin =
4411 gst_sdp_message_get_origin (pending_remote->sdp);
4412 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
4413 offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
4415 gst_sdp_message_set_session_name (ret, "-");
4417 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
4418 const GstSDPAttribute *attr =
4419 gst_sdp_message_get_attribute (pending_remote->sdp, i);
4421 if (g_strcmp0 (attr->key, "ice-options") == 0) {
4422 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
4426 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
4427 GstSDPMedia *media = NULL;
4428 GstSDPMedia *offer_media;
4429 GstWebRTCDTLSSetup offer_setup, answer_setup;
4431 gboolean bundle_only;
4435 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
4436 bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
4438 gst_sdp_media_new (&media);
4439 if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
4440 gst_sdp_media_set_port_info (media, 0, 0);
4442 gst_sdp_media_set_port_info (media, 9, 0);
4443 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
4448 /* FIXME: deal with ICE restarts */
4449 if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
4450 ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
4451 pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
4454 _generate_ice_credentials (&ufrag, &pwd);
4456 ufrag = g_strdup (bundle_ufrag);
4457 pwd = g_strdup (bundle_pwd);
4460 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
4461 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
4466 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
4467 const GstSDPAttribute *attr =
4468 gst_sdp_media_get_attribute (offer_media, j);
4470 if (g_strcmp0 (attr->key, "mid") == 0
4471 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
4472 gst_sdp_media_add_attribute (media, attr->key, attr->value);
4473 /* FIXME: handle anything we want to keep */
4477 mid = gst_sdp_media_get_attribute_val (media, "mid");
4478 /* XXX: not strictly required but a lot of functionality requires a mid */
4481 /* set the a=setup: attribute */
4482 offer_setup = _get_dtls_setup_from_media (offer_media);
4483 answer_setup = _intersect_dtls_setup (offer_setup);
4484 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4485 GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
4486 "transceiver direction");
4489 _media_replace_setup (media, answer_setup);
4491 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
4494 if (gst_sdp_media_formats_len (offer_media) != 1) {
4495 GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
4496 "for webrtc-datachannel");
4499 sctp_port = _get_sctp_port_from_media (offer_media);
4500 if (sctp_port == -1) {
4501 GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
4505 /* XXX: older browsers will produce a different SDP format for data
4506 * channel that is currently not parsed correctly */
4507 gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
4509 gst_sdp_media_set_media (media, "application");
4510 gst_sdp_media_set_port_info (media, 9, 0);
4511 gst_sdp_media_add_format (media, "webrtc-datachannel");
4513 /* FIXME: negotiate this properly on renegotiation */
4514 gst_sdp_media_add_attribute (media, "sctp-port", "5000");
4516 _get_or_create_data_channel_transports (webrtc,
4517 bundled_mids ? bundle_idx : i);
4521 g_string_append_printf (bundled_mids, " %s", mid);
4524 _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
4526 } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
4527 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
4528 GstCaps *offer_caps, *answer_caps = NULL;
4529 GstWebRTCRTPTransceiver *rtp_trans = NULL;
4530 WebRTCTransceiver *trans = NULL;
4531 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
4532 gint target_pt = -1;
4533 gint original_target_pt = -1;
4534 guint target_ssrc = 0;
4536 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
4537 offer_caps = _rtp_caps_from_media (offer_media);
4539 if (last_answer && i < gst_sdp_message_medias_len (last_answer)
4540 && (rtp_trans = _find_transceiver_for_mid (webrtc, mid))) {
4541 const GstSDPMedia *last_media =
4542 gst_sdp_message_get_media (last_answer, i);
4543 const gchar *last_mid =
4544 gst_sdp_media_get_attribute_val (last_media, "mid");
4545 GstCaps *current_caps;
4547 /* FIXME: assumes no shenanigans with recycling transceivers */
4548 g_assert (g_strcmp0 (mid, last_mid) == 0);
4550 current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4552 gst_caps_unref (offer_caps);
4556 current_caps = _rtp_caps_from_media (last_media);
4559 answer_caps = gst_caps_intersect (offer_caps, current_caps);
4560 if (gst_caps_is_empty (answer_caps)) {
4561 GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
4562 GST_PTR_FORMAT ") don't intersect with caps from codec"
4563 " preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
4565 gst_caps_unref (current_caps);
4566 gst_caps_unref (answer_caps);
4567 gst_caps_unref (offer_caps);
4570 gst_caps_unref (current_caps);
4573 /* XXX: In theory we're meant to use the sendrecv formats for the
4574 * inactive direction however we don't know what that may be and would
4575 * require asking outside what it expects to possibly send later */
4577 GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
4578 "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
4579 "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
4581 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
4582 GstCaps *trans_caps;
4584 rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
4586 if (g_list_find (seen_transceivers, rtp_trans)) {
4587 /* Don't double allocate a transceiver to multiple mlines */
4592 trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
4594 gst_caps_unref (offer_caps);
4598 GST_LOG_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4599 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4601 /* FIXME: technically this is a little overreaching as some fields we
4602 * we can deal with not having and/or we may have unrecognized fields
4603 * that we cannot actually support */
4605 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4606 gst_caps_unref (trans_caps);
4608 if (!gst_caps_is_empty (answer_caps)) {
4609 GST_LOG_OBJECT (webrtc,
4610 "found compatible transceiver %" GST_PTR_FORMAT
4611 " for offer media %u", rtp_trans, i);
4614 gst_caps_unref (answer_caps);
4623 answer_dir = rtp_trans->direction;
4624 g_assert (answer_caps != NULL);
4626 /* if no transceiver, then we only receive that stream and respond with
4627 * the intersection with the transceivers codec preferences caps */
4628 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
4629 GST_WARNING_OBJECT (webrtc, "did not find compatible transceiver for "
4630 "offer caps %" GST_PTR_FORMAT ", will only receive", offer_caps);
4634 GstCaps *trans_caps;
4635 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
4637 if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
4638 kind = GST_WEBRTC_KIND_AUDIO;
4639 else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
4641 kind = GST_WEBRTC_KIND_VIDEO;
4643 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
4644 GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
4646 trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
4647 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4649 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
4650 " for mline %u with media kind %d", trans, i, kind);
4652 trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4654 gst_caps_unref (offer_caps);
4658 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4659 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4661 /* FIXME: technically this is a little overreaching as some fields we
4662 * we can deal with not having and/or we may have unrecognized fields
4663 * that we cannot actually support */
4665 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4666 gst_clear_caps (&trans_caps);
4668 answer_caps = gst_caps_ref (offer_caps);
4671 trans = WEBRTC_TRANSCEIVER (rtp_trans);
4674 seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
4676 if (gst_caps_is_empty (answer_caps)) {
4677 GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
4678 gst_clear_caps (&answer_caps);
4679 gst_clear_caps (&offer_caps);
4683 if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps)) {
4684 GstWebRTCKind caps_kind = webrtc_kind_from_caps (answer_caps);
4686 GST_WARNING_OBJECT (webrtc,
4687 "Trying to change kind of transceiver %" GST_PTR_FORMAT
4688 " at m-line %d from %s (%d) to %s (%d)", trans, rtp_trans->mline,
4689 gst_webrtc_kind_to_string (rtp_trans->kind), rtp_trans->kind,
4690 gst_webrtc_kind_to_string (caps_kind), caps_kind);
4693 answer_caps = gst_caps_make_writable (answer_caps);
4694 for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
4695 GstStructure *s = gst_caps_get_structure (answer_caps, k);
4696 /* taken from the offer sdp already and already intersected above */
4697 gst_structure_remove_field (s, "a-mid");
4698 if (!trans->do_nack)
4699 gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
4702 if (gst_sdp_media_set_media_from_caps (answer_caps, media) != GST_SDP_OK) {
4703 GST_WARNING_OBJECT (webrtc,
4704 "Could not build media from caps %" GST_PTR_FORMAT, answer_caps);
4705 gst_clear_caps (&answer_caps);
4706 gst_clear_caps (&offer_caps);
4710 _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
4713 original_target_pt = target_pt;
4715 _media_add_fec (media, trans, offer_caps, &target_pt);
4716 if (trans->do_nack) {
4717 _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
4718 if (target_pt != original_target_pt)
4719 _media_add_rtx (media, trans, offer_caps, original_target_pt,
4723 if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
4724 _media_add_ssrcs (media, answer_caps, webrtc,
4725 WEBRTC_TRANSCEIVER (rtp_trans));
4727 gst_caps_unref (answer_caps);
4730 /* set the new media direction */
4731 offer_dir = _get_direction_from_media (offer_media);
4732 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
4733 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4734 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
4735 "transceiver direction");
4736 gst_caps_unref (offer_caps);
4739 _media_replace_direction (media, answer_dir);
4741 if (!trans->stream) {
4742 TransportStream *item;
4745 _get_or_create_transport_stream (webrtc,
4746 bundled_mids ? bundle_idx : i, FALSE);
4747 webrtc_transceiver_set_transport (trans, item);
4751 const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
4754 g_string_append_printf (bundled_mids, " %s", mid);
4757 /* set the a=fingerprint: for this transport */
4758 _add_fingerprint_to_media (trans->stream->transport, media);
4760 gst_caps_unref (offer_caps);
4762 GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
4768 if (error && *error)
4769 GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
4771 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
4772 gst_sdp_media_free (media);
4773 gst_sdp_media_copy (offer_media, &media);
4774 gst_sdp_media_set_port_info (media, 0, 0);
4775 /* Clear error here as it is not propagated to the caller and the media
4776 * is just skipped, i.e. more iterations are going to happen. */
4777 g_clear_error (error);
4779 gst_sdp_message_add_media (ret, media);
4780 gst_sdp_media_free (media);
4784 gchar *mids = g_string_free (bundled_mids, FALSE);
4786 gst_sdp_message_add_attribute (ret, "group", mids);
4791 g_free (bundle_ufrag);
4794 g_free (bundle_pwd);
4796 /* FIXME: can we add not matched transceivers? */
4798 /* XXX: only true for the initial offerer */
4799 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
4802 g_strfreev (bundled);
4804 g_list_free (seen_transceivers);
4806 if (webrtc->priv->last_generated_offer)
4807 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4808 webrtc->priv->last_generated_offer = NULL;
4809 if (webrtc->priv->last_generated_answer)
4810 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4812 GstSDPMessage *copy;
4813 gst_sdp_message_copy (ret, ©);
4814 webrtc->priv->last_generated_answer =
4815 gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
4823 GstStructure *options;
4824 GstWebRTCSDPType type;
4827 static GstStructure *
4828 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
4830 GstWebRTCSessionDescription *desc = NULL;
4831 GstSDPMessage *sdp = NULL;
4832 GstStructure *s = NULL;
4833 GError *error = NULL;
4835 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
4836 gst_webrtc_sdp_type_to_string (data->type), data->options);
4838 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
4839 sdp = _create_offer_task (webrtc, data->options, &error);
4840 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
4841 sdp = _create_answer_task (webrtc, data->options, &error);
4843 g_assert_not_reached ();
4848 desc = gst_webrtc_session_description_new (data->type, sdp);
4849 s = gst_structure_new ("application/x-gst-promise",
4850 gst_webrtc_sdp_type_to_string (data->type),
4851 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
4853 g_warn_if_fail (error != NULL);
4854 GST_WARNING_OBJECT (webrtc, "returning error: %s",
4855 error ? error->message : "Unknown");
4856 s = gst_structure_new ("application/x-gst-promise",
4857 "error", G_TYPE_ERROR, error, NULL);
4858 g_clear_error (&error);
4864 gst_webrtc_session_description_free (desc);
4870 _free_create_sdp_data (struct create_sdp *data)
4873 gst_structure_free (data->options);
4878 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
4879 const GstStructure * options, GstPromise * promise)
4881 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4884 data->options = gst_structure_copy (options);
4885 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
4887 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4888 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4890 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4891 "Could not create offer. webrtcbin is closed");
4892 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4893 "error", G_TYPE_ERROR, error, NULL);
4895 gst_promise_reply (promise, s);
4897 g_clear_error (&error);
4902 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
4903 const GstStructure * options, GstPromise * promise)
4905 struct create_sdp *data = g_new0 (struct create_sdp, 1);
4908 data->options = gst_structure_copy (options);
4909 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
4911 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4912 data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4914 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
4915 "Could not create answer. webrtcbin is closed.");
4916 GstStructure *s = gst_structure_new ("application/x-gst-promise",
4917 "error", G_TYPE_ERROR, error, NULL);
4919 gst_promise_reply (promise, s);
4921 g_clear_error (&error);
4925 static GstWebRTCBinPad *
4926 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
4927 GstWebRTCRTPTransceiver * trans, guint serial, char *msid)
4929 GstWebRTCBinPad *pad;
4932 if (direction == GST_PAD_SINK) {
4933 if (serial == G_MAXUINT)
4934 serial = webrtc->priv->max_sink_pad_serial++;
4936 serial = webrtc->priv->src_pad_counter++;
4940 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
4942 pad = gst_webrtc_bin_pad_new (pad_name, direction, msid);
4945 pad->trans = gst_object_ref (trans);
4950 static GstWebRTCRTPTransceiver *
4951 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
4952 const GstSDPMessage * sdp, guint media_idx)
4954 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4955 GstWebRTCRTPTransceiver *ret = NULL;
4958 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4959 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4961 if (g_strcmp0 (attr->key, "mid") == 0) {
4962 if ((ret = _find_transceiver_for_mid (webrtc, attr->value)))
4967 ret = _find_transceiver (webrtc, &media_idx,
4968 (FindTransceiverFunc) transceiver_match_for_mline);
4971 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
4976 _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
4978 GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4979 guint ulpfec_pt = 0, red_pt = 0;
4980 GstPad *sinkpad, *srcpad, *ghost;
4983 if (trans->stream) {
4985 transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
4986 red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
4989 if (trans->ulpfecenc || trans->redenc) {
4990 g_critical ("webrtcbin: duplicate call to create a fec encoder or "
4995 GST_DEBUG_OBJECT (webrtc,
4996 "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
4999 ret = gst_bin_new (NULL);
5001 trans->ulpfecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
5002 gst_object_ref_sink (trans->ulpfecenc);
5003 if (!gst_bin_add (GST_BIN (ret), trans->ulpfecenc))
5004 g_warn_if_reached ();
5005 sinkpad = gst_element_get_static_pad (trans->ulpfecenc, "sink");
5007 g_object_bind_property (rtp_trans, "fec-percentage", trans->ulpfecenc,
5008 "percentage", G_BINDING_DEFAULT);
5010 trans->redenc = gst_element_factory_make ("rtpredenc", NULL);
5011 gst_object_ref_sink (trans->redenc);
5013 GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
5014 rtp_trans->mline, red_pt);
5016 gst_bin_add (GST_BIN (ret), trans->redenc);
5017 gst_element_link (trans->ulpfecenc, trans->redenc);
5019 ghost = gst_ghost_pad_new ("sink", sinkpad);
5020 gst_clear_object (&sinkpad);
5021 gst_element_add_pad (ret, ghost);
5024 srcpad = gst_element_get_static_pad (trans->redenc, "src");
5025 ghost = gst_ghost_pad_new ("src", srcpad);
5026 gst_clear_object (&srcpad);
5027 gst_element_add_pad (ret, ghost);
5034 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
5036 GstStructure *s = user_data;
5038 gst_structure_id_set_value (s, field_id, value);
5043 #define GST_WEBRTC_PAYLOAD_TYPE "gst.webrtcbin.payload.type"
5046 try_match_transceiver_with_fec_decoder (GstWebRTCBin * webrtc,
5047 WebRTCTransceiver * trans)
5051 for (l = trans->stream->fecdecs; l; l = l->next) {
5052 GstElement *fecdec = GST_ELEMENT (l->data);
5053 gboolean found_transceiver = FALSE;
5058 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (fecdec),
5059 GST_WEBRTC_PAYLOAD_TYPE));
5060 if (original_pt <= 0) {
5061 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
5062 "transceiver, fec decoder %" GST_PTR_FORMAT " does not contain a "
5063 "valid payload type", fecdec);
5067 for (i = 0; i < trans->stream->ptmap->len; i++) {
5068 PtMapItem *item = &g_array_index (trans->stream->ptmap, PtMapItem, i);
5070 /* FIXME: this only works for a 1-1 original_pt->fec_pt mapping */
5071 if (original_pt == item->pt && item->media_idx != -1
5072 && item->media_idx == trans->parent.mline) {
5073 if (trans->ulpfecdec) {
5074 GST_FIXME_OBJECT (trans, "cannot");
5075 gst_clear_object (&trans->ulpfecdec);
5077 trans->ulpfecdec = gst_object_ref (fecdec);
5078 found_transceiver = TRUE;
5083 if (!found_transceiver) {
5084 GST_WARNING_OBJECT (trans, "failed to match fec decoder with "
5091 _set_internal_rtpbin_element_props_from_stream (GstWebRTCBin * webrtc,
5092 TransportStream * stream)
5094 GstStructure *merged_local_rtx_ssrc_map;
5095 GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
5096 GValue red_pt_array = { 0, };
5101 gst_value_array_init (&red_pt_array, 0);
5103 rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
5104 GST_DEBUG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
5106 for (i = 0; i < rtx_count; i++) {
5107 GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
5108 const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
5109 const gchar *apt = gst_structure_get_string (s, "apt");
5111 GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
5112 gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
5115 GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
5116 GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
5117 stream->rtxsend, pt_map);
5119 if (stream->rtxreceive)
5120 g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
5121 if (stream->rtxsend)
5122 g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
5124 gst_structure_free (pt_map);
5125 g_clear_pointer (&rtx_pt, g_free);
5127 merged_local_rtx_ssrc_map =
5128 gst_structure_new_empty ("application/x-rtp-ssrc-map");
5130 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
5131 GstWebRTCRTPTransceiver *rtp_trans =
5132 g_ptr_array_index (webrtc->priv->transceivers, i);
5133 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
5135 if (trans->stream == stream) {
5136 gint ulpfec_pt, red_pt = 0;
5138 ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC", rtp_trans->mline);
5142 red_pt = transport_stream_get_pt (stream, "RED", rtp_trans->mline);
5146 GValue ptval = { 0, };
5148 g_value_init (&ptval, G_TYPE_INT);
5149 g_value_set_int (&ptval, red_pt);
5150 gst_value_array_append_value (&red_pt_array, &ptval);
5151 g_value_unset (&ptval);
5154 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " transceiver %"
5155 GST_PTR_FORMAT " has FEC payload %d and RED payload %d", stream,
5156 trans, ulpfec_pt, red_pt);
5158 if (trans->ulpfecenc) {
5159 guint ulpfecenc_pt = ulpfec_pt;
5161 if (ulpfecenc_pt == 0)
5164 g_object_set (trans->ulpfecenc, "pt", ulpfecenc_pt, "multipacket",
5165 rtp_trans->kind == GST_WEBRTC_KIND_VIDEO, "percentage",
5166 trans->fec_percentage, NULL);
5169 try_match_transceiver_with_fec_decoder (webrtc, trans);
5170 if (trans->ulpfecdec) {
5171 g_object_set (trans->ulpfecdec, "passthrough", ulpfec_pt == 0, "pt",
5175 if (trans->redenc) {
5176 gboolean always_produce = TRUE;
5178 /* passthrough settings */
5180 always_produce = FALSE;
5182 g_object_set (trans->redenc, "pt", red_pt, "allow-no-red-blocks",
5183 always_produce, NULL);
5186 if (trans->local_rtx_ssrc_map) {
5187 gst_structure_foreach (trans->local_rtx_ssrc_map,
5188 _merge_structure, merged_local_rtx_ssrc_map);
5193 if (stream->rtxsend)
5194 g_object_set (stream->rtxsend, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
5195 gst_clear_structure (&merged_local_rtx_ssrc_map);
5197 if (stream->reddec) {
5198 g_object_set_property (G_OBJECT (stream->reddec), "payloads",
5202 g_value_unset (&red_pt_array);
5205 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
5207 _insert_netsim_element_between (GstWebRTCBin * webrtc, GstElement * srcbin,
5208 const gchar * srcpadname, GstElement * sinkbin, const gchar * sinkpadname,
5211 gboolean send = !g_strcmp0 (sinkpadname, "rtp_sink");
5212 gchar *netsim_name = g_strdup_printf ("netsim_%s_%u",
5213 send ? "send" : "recv", idx);
5214 GstElement *netsim = gst_element_factory_make ("netsim", netsim_name);
5215 g_free (netsim_name);
5217 gst_bin_add (GST_BIN (webrtc), netsim);
5218 g_object_set (netsim, "drop-probability",
5219 send ? webrtc->priv->drop_probability_sender :
5220 webrtc->priv->drop_probability_receiver, NULL);
5221 gst_element_sync_state_with_parent (netsim);
5223 if (!gst_element_link_pads (srcbin, srcpadname, netsim, "sink"))
5224 g_warn_if_reached ();
5226 if (!gst_element_link_pads (netsim, "src", sinkbin, sinkpadname))
5227 g_warn_if_reached ();
5232 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
5237 * ,--------------------------------------------webrtcbin--------------------------------------------,
5239 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
5240 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
5241 * ; ,---clocksync---, ; ; ; ; ;
5242 * ; ; ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
5243 * ; sink_%u ; ; ,---fec encoder---, ; ; '---------------------' ;
5244 * o---------o sink src o-o sink src o--o send_rtp_sink_%u ; ;
5245 * ; '---------------' ,-----------------, '--------------------' ;
5246 * '-------------------------------------------------------------------------------------------------'
5251 * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
5253 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
5254 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
5256 * ; sink_%u ,---clocksync---, ,---fec encoder---, ,---funnel---, ; send_rtcp_src_%u o---o rtcp_sink ; ;
5257 * o----------o sink src o-o sink src o--o sink_%u ; ; ; '---------------------' ;
5258 * ; '---------------' ,-----------------, ; ; ; ; ;
5259 * ; ; src o-o send_rtp_sink_%u ; ;
5260 * ; sink_%u ,---clocksync---, ,---fec encoder---, ; ; ; ; ;
5261 * o----------o sink src o-o sink src o--o sink%u ; '--------------------' ;
5262 * ; '---------------' ,-----------------, '------------' ;
5263 * '-----------------------------------------------------------------------------------------------------------------'
5265 GstPadTemplate *rtp_templ;
5266 GstPad *rtp_sink, *sinkpad, *srcpad;
5268 WebRTCTransceiver *trans;
5269 GstElement *clocksync;
5270 GstElement *fec_encoder;
5272 g_return_val_if_fail (pad->trans != NULL, NULL);
5274 trans = WEBRTC_TRANSCEIVER (pad->trans);
5276 GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
5278 g_assert (trans->stream);
5280 clocksync = gst_element_factory_make ("clocksync", NULL);
5281 g_object_set (clocksync, "sync", TRUE, NULL);
5282 gst_bin_add (GST_BIN (webrtc), clocksync);
5283 gst_element_sync_state_with_parent (clocksync);
5285 srcpad = gst_element_get_static_pad (clocksync, "src");
5287 fec_encoder = _build_fec_encoder (webrtc, trans);
5289 g_warn_if_reached ();
5293 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
5295 gst_bin_add (GST_BIN (webrtc), fec_encoder);
5296 gst_element_sync_state_with_parent (fec_encoder);
5298 sinkpad = gst_element_get_static_pad (fec_encoder, "sink");
5299 if (gst_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)
5300 g_warn_if_reached ();
5301 gst_clear_object (&srcpad);
5302 gst_clear_object (&sinkpad);
5303 sinkpad = gst_element_get_static_pad (clocksync, "sink");
5304 srcpad = gst_element_get_static_pad (fec_encoder, "src");
5306 if (!webrtc->rtpfunnel) {
5308 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
5309 "send_rtp_sink_%u");
5310 g_assert (rtp_templ);
5312 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
5314 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
5316 gst_pad_link (srcpad, rtp_sink);
5317 gst_object_unref (rtp_sink);
5319 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
5320 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
5321 if (webrtc->priv->netsim) {
5322 _insert_netsim_element_between (webrtc, GST_ELEMENT (webrtc->rtpbin), pad_name,
5323 GST_ELEMENT (trans->stream->send_bin), "rtp_sink", pad->trans->mline);
5326 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5327 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
5328 g_warn_if_reached ();
5329 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
5334 gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
5335 GstPad *funnel_sinkpad =
5336 gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
5338 gst_pad_link (srcpad, funnel_sinkpad);
5341 gst_object_unref (funnel_sinkpad);
5344 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
5346 gst_clear_object (&srcpad);
5347 gst_clear_object (&sinkpad);
5349 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
5351 return GST_PAD (pad);
5354 /* output pads are receiving elements */
5356 _connect_output_stream (GstWebRTCBin * webrtc,
5357 TransportStream * stream, guint session_id)
5360 * ,------------------------webrtcbin------------------------,
5361 * ; ,---------rtpbin---------, ;
5362 * ; ,-transport_receive_%u--, ; ; ;
5363 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
5365 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
5366 * ; '-----------------------' ; ; ; src_%u
5367 * ; ; recv_rtp_src_%u_%u_%u o--o
5368 * ; '------------------------' ;
5369 * '---------------------------------------------------------'
5373 if (stream->output_connected) {
5374 GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
5375 "connected to rtpbin. Not connecting", stream);
5379 GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
5380 session_id, stream);
5382 pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
5383 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
5384 if (webrtc->priv->netsim) {
5385 _insert_netsim_element_between (webrtc, GST_ELEMENT (stream->receive_bin),
5386 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name, session_id);
5389 if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
5390 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
5391 g_warn_if_reached ();
5392 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
5397 gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
5399 /* The webrtcbin src_%u output pads will be created when rtpbin receives
5400 * data on that stream in on_rtpbin_pad_added() */
5402 stream->output_connected = TRUE;
5412 _clear_ice_candidate_item (IceCandidateItem * item)
5414 g_free (item->candidate);
5418 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
5419 gboolean drop_invalid)
5421 GstWebRTCICEStream *stream;
5423 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
5424 if (stream == NULL) {
5426 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
5429 IceCandidateItem new;
5430 new.mlineindex = item->mlineindex;
5431 new.candidate = g_strdup (item->candidate);
5432 GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
5435 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5436 ICE_UNLOCK (webrtc);
5441 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
5442 item->mlineindex, item->candidate);
5444 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
5448 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
5449 const GstSDPMedia * media)
5452 GstWebRTCICEStream *stream = NULL;
5454 for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
5455 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
5456 if (g_strcmp0 (attr->key, "candidate") == 0) {
5460 stream = _find_ice_stream_for_session (webrtc, mlineindex);
5461 if (stream == NULL) {
5462 GST_DEBUG_OBJECT (webrtc,
5463 "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
5467 candidate = g_strdup_printf ("a=candidate:%s", attr->value);
5468 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
5469 mlineindex, candidate);
5470 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
5477 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
5478 GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
5480 GstSDPMedia *media = NULL;
5482 if (mline_index < sdp->medias->len) {
5483 media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
5486 if (media == NULL) {
5487 GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
5491 // Add the candidate as an attribute, first stripping off the existing
5492 // candidate: key from the string description
5493 if (strlen (candidate) < 10) {
5494 GST_WARNING_OBJECT (webrtc,
5495 "Dropping invalid ICE candidate for mline %d: %s", mline_index,
5499 gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
5503 _filter_sdp_fields (GQuark field_id, const GValue * value,
5504 GstStructure * new_structure)
5506 if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
5507 gst_structure_id_set_value (new_structure, field_id, value);
5513 transport_stream_ptmap_get_rtp_header_extension_id (TransportStream * stream,
5514 const char *rtphdrext_uri)
5518 for (i = 0; i < stream->ptmap->len; i++) {
5519 PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
5522 id = caps_get_rtp_header_extension_id (item->caps, rtphdrext_uri);
5531 ensure_rtx_hdr_ext (TransportStream * stream)
5533 stream->rtphdrext_id_stream_id =
5534 transport_stream_ptmap_get_rtp_header_extension_id (stream,
5535 RTPHDREXT_STREAM_ID);
5536 stream->rtphdrext_id_repaired_stream_id =
5537 transport_stream_ptmap_get_rtp_header_extension_id (stream,
5538 RTPHDREXT_REPAIRED_STREAM_ID);
5540 /* TODO: removing header extensions usage from rtx on renegotiation */
5542 if (stream->rtxsend) {
5543 if (stream->rtphdrext_id_stream_id != -1 && !stream->rtxsend_stream_id) {
5544 stream->rtxsend_stream_id =
5545 gst_rtp_header_extension_create_from_uri (RTPHDREXT_STREAM_ID);
5546 if (!stream->rtxsend_stream_id)
5547 g_warn_if_reached ();
5548 gst_rtp_header_extension_set_id (stream->rtxsend_stream_id,
5549 stream->rtphdrext_id_stream_id);
5551 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5552 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_stream_id,
5553 stream->rtphdrext_id_stream_id, stream->rtxsend);
5555 g_signal_emit_by_name (stream->rtxsend, "add-extension",
5556 stream->rtxsend_stream_id);
5559 if (stream->rtphdrext_id_repaired_stream_id != -1
5560 && !stream->rtxsend_repaired_stream_id) {
5561 stream->rtxsend_repaired_stream_id =
5562 gst_rtp_header_extension_create_from_uri
5563 (RTPHDREXT_REPAIRED_STREAM_ID);
5564 if (!stream->rtxsend_repaired_stream_id)
5565 g_warn_if_reached ();
5566 gst_rtp_header_extension_set_id (stream->rtxsend_repaired_stream_id,
5567 stream->rtphdrext_id_repaired_stream_id);
5569 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5570 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_repaired_stream_id,
5571 stream->rtphdrext_id_repaired_stream_id, stream->rtxsend);
5573 g_signal_emit_by_name (stream->rtxsend, "add-extension",
5574 stream->rtxsend_repaired_stream_id);
5578 if (stream->rtxreceive) {
5579 if (stream->rtphdrext_id_stream_id != -1 && !stream->rtxreceive_stream_id) {
5580 stream->rtxreceive_stream_id =
5581 gst_rtp_header_extension_create_from_uri (RTPHDREXT_STREAM_ID);
5582 if (!stream->rtxreceive_stream_id)
5583 g_warn_if_reached ();
5584 gst_rtp_header_extension_set_id (stream->rtxreceive_stream_id,
5585 stream->rtphdrext_id_stream_id);
5587 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
5588 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5589 " with id %u to %" GST_PTR_FORMAT, stream->rtxreceive_stream_id,
5590 stream->rtphdrext_id_stream_id, stream->rtxreceive);
5592 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5593 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_stream_id,
5594 stream->rtphdrext_id_stream_id, stream->rtxreceive);
5597 g_signal_emit_by_name (stream->rtxreceive, "add-extension",
5598 stream->rtxreceive_stream_id);
5601 if (stream->rtphdrext_id_repaired_stream_id != -1
5602 && !stream->rtxreceive_repaired_stream_id) {
5603 stream->rtxreceive_repaired_stream_id =
5604 gst_rtp_header_extension_create_from_uri
5605 (RTPHDREXT_REPAIRED_STREAM_ID);
5606 if (!stream->rtxreceive_repaired_stream_id)
5607 g_warn_if_reached ();
5608 gst_rtp_header_extension_set_id (stream->rtxreceive_repaired_stream_id,
5609 stream->rtphdrext_id_repaired_stream_id);
5611 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
5612 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5613 " with id %u to %" GST_PTR_FORMAT, stream->rtxreceive_repaired_stream_id,
5614 stream->rtphdrext_id_repaired_stream_id, stream->rtxreceive);
5616 GST_DEBUG_OBJECT (stream, "adding rtp header extension %" GST_PTR_FORMAT
5617 " with id %u to %" GST_PTR_FORMAT, stream->rtxsend_repaired_stream_id,
5618 stream->rtphdrext_id_repaired_stream_id, stream->rtxreceive);
5621 g_signal_emit_by_name (stream->rtxreceive, "add-extension",
5622 stream->rtxreceive_repaired_stream_id);
5628 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
5629 TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
5633 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5636 proto = gst_sdp_media_get_proto (media);
5637 if (proto != NULL) {
5638 /* Parse global SDP attributes once */
5639 GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
5640 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
5641 gst_sdp_message_attributes_to_caps (sdp, global_caps);
5642 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
5643 gst_sdp_media_attributes_to_caps (media, global_caps);
5645 len = gst_sdp_media_formats_len (media);
5646 for (i = 0; i < len; i++) {
5647 GstCaps *caps, *outcaps;
5653 pt = atoi (gst_sdp_media_get_format (media, i));
5655 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
5658 caps = gst_sdp_media_get_caps_from_media (media, pt);
5660 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
5664 /* Merge in global caps */
5665 /* Intersect will merge in missing fields to the current caps */
5666 outcaps = gst_caps_intersect (caps, global_caps);
5667 gst_caps_unref (caps);
5669 s = gst_caps_get_structure (outcaps, 0);
5670 gst_structure_set_name (s, "application/x-rtp");
5671 if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
5672 gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
5674 item.caps = gst_caps_new_empty ();
5676 for (j = 0; j < gst_caps_get_size (outcaps); j++) {
5677 GstStructure *s = gst_caps_get_structure (outcaps, j);
5678 GstStructure *filtered =
5679 gst_structure_new_empty (gst_structure_get_name (s));
5681 gst_structure_foreach (s,
5682 (GstStructureForeachFunc) _filter_sdp_fields, filtered);
5683 gst_caps_append_structure (item.caps, filtered);
5687 item.media_idx = media_idx;
5688 gst_caps_unref (outcaps);
5690 g_array_append_val (stream->ptmap, item);
5693 gst_caps_unref (global_caps);
5698 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
5699 const GstSDPMessage * sdp, guint media_idx,
5700 TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
5701 GStrv bundled, guint bundle_idx, GError ** error)
5703 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
5704 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
5705 GstWebRTCRTPTransceiverDirection new_dir;
5706 const GstSDPMedia *local_media, *remote_media;
5707 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
5708 GstWebRTCDTLSSetup new_setup;
5709 char *local_msid = NULL;
5710 gboolean new_rtcp_rsize;
5711 ReceiveState receive_state = RECEIVE_STATE_UNSET;
5715 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5718 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5721 rtp_trans->mline = media_idx;
5723 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
5724 if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
5725 GST_FIXME_OBJECT (webrtc, "Updating video transceiver %" GST_PTR_FORMAT
5726 " to audio, which isn't fully supported.", rtp_trans);
5727 rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
5730 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
5731 if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
5732 GST_FIXME_OBJECT (webrtc, "Updating audio transceiver %" GST_PTR_FORMAT
5733 " to video, which isn't fully supported.", rtp_trans);
5734 rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
5737 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
5738 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
5740 if (g_strcmp0 (attr->key, "mid") == 0) {
5741 g_free (rtp_trans->mid);
5742 rtp_trans->mid = g_strdup (attr->value);
5747 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
5748 GstWebRTCDTLSSetup local_setup, remote_setup;
5750 local_setup = _get_dtls_setup_from_media (local_media);
5751 remote_setup = _get_dtls_setup_from_media (remote_media);
5752 new_setup = _get_final_setup (local_setup, remote_setup);
5753 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5754 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5755 "Cannot intersect direction attributes for media %u", media_idx);
5759 local_dir = _get_direction_from_media (local_media);
5760 remote_dir = _get_direction_from_media (remote_media);
5761 new_dir = _get_final_direction (local_dir, remote_dir);
5762 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
5763 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
5764 "Cannot intersect dtls setup attributes for media %u", media_idx);
5768 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
5769 && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
5770 && prev_dir != new_dir) {
5771 g_set_error (error, GST_WEBRTC_ERROR,
5772 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
5773 "transceiver direction changes are not implemented. Media %u",
5778 if (!bundled || bundle_idx == media_idx) {
5779 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
5780 && _media_has_attribute_key (remote_media, "rtcp-rsize");
5784 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
5785 media_idx, &session);
5787 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
5788 g_object_unref (session);
5794 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5796 /* Not a bundled stream means this entire transport is inactive,
5797 * so set the receive state to BLOCK below */
5798 stream->active = FALSE;
5799 receive_state = RECEIVE_STATE_BLOCK;
5802 /* If this transceiver is active for sending or receiving,
5803 * we still need receive at least RTCP, so need to unblock
5804 * the receive bin below. */
5805 GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
5806 receive_state = RECEIVE_STATE_PASS;
5807 stream->active = TRUE;
5810 if (new_dir != prev_dir) {
5811 guint rtp_session_id = bundled ? bundle_idx : media_idx;
5813 GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
5814 " direction change from %s to %s", rtp_trans,
5815 gst_webrtc_rtp_transceiver_direction_to_string (prev_dir),
5816 gst_webrtc_rtp_transceiver_direction_to_string (new_dir));
5818 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
5819 GstWebRTCBinPad *pad;
5821 pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
5823 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
5825 GstPad *peer = gst_pad_get_peer (target);
5827 gst_pad_send_event (peer, gst_event_new_eos ());
5828 gst_object_unref (peer);
5830 gst_object_unref (target);
5832 gst_object_unref (pad);
5835 /* XXX: send eos event up the sink pad as well? */
5838 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
5839 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5840 GstWebRTCBinPad *pad =
5841 _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
5842 local_msid = _get_msid_from_media (local_media);
5845 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
5846 " for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans,
5848 if (g_strcmp0 (pad->msid, local_msid) != 0) {
5849 GST_DEBUG_OBJECT (webrtc, "send pad %" GST_PTR_FORMAT
5850 " transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'"
5851 " to \'%s\'", pad, trans, pad->msid, local_msid);
5852 g_clear_pointer (&pad->msid, g_free);
5853 pad->msid = local_msid;
5854 g_object_notify (G_OBJECT (pad), "msid");
5857 g_clear_pointer (&local_msid, g_free);
5859 gst_object_unref (pad);
5861 GST_DEBUG_OBJECT (webrtc,
5862 "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
5863 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
5864 G_MAXUINT, local_msid);
5866 _connect_input_stream (webrtc, pad);
5867 _add_pad (webrtc, pad);
5870 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
5871 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5872 GstWebRTCBinPad *pad =
5873 _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
5874 char *remote_msid = _get_msid_from_media (remote_media);
5877 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
5878 " for transceiver %" GST_PTR_FORMAT " with msid \'%s\'", pad, trans,
5880 if (g_strcmp0 (pad->msid, remote_msid) != 0) {
5881 GST_DEBUG_OBJECT (webrtc, "receive pad %" GST_PTR_FORMAT
5882 " transceiver %" GST_PTR_FORMAT " changing msid from \'%s\'"
5883 " to \'%s\'", pad, trans, pad->msid, remote_msid);
5884 g_clear_pointer (&pad->msid, g_free);
5885 pad->msid = remote_msid;
5887 g_object_notify (G_OBJECT (pad), "msid");
5889 g_clear_pointer (&remote_msid, g_free);
5891 gst_object_unref (pad);
5893 GST_DEBUG_OBJECT (webrtc,
5894 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
5895 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
5896 G_MAXUINT, remote_msid);
5899 if (!trans->stream) {
5900 TransportStream *item;
5903 _get_or_create_transport_stream (webrtc, rtp_session_id, FALSE);
5904 webrtc_transceiver_set_transport (trans, item);
5907 _connect_output_stream (webrtc, trans->stream, rtp_session_id);
5908 /* delay adding the pad until rtpbin creates the recv output pad
5909 * to ghost to so queries/events travel through the pipeline correctly
5910 * as soon as the pad is added */
5911 _add_pad_to_list (webrtc, pad);
5915 rtp_trans->mline = media_idx;
5916 rtp_trans->current_direction = new_dir;
5919 if (!bundled || bundle_idx == media_idx) {
5920 if (stream->rtxsend || stream->rtxreceive) {
5921 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
5924 g_object_set (stream, "dtls-client",
5925 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5928 /* Must be after setting the "dtls-client" so that data is not pushed into
5929 * the dtlssrtp elements before the ssl direction has been set which will
5930 * throw SSL errors */
5931 if (receive_state != RECEIVE_STATE_UNSET)
5932 transport_receive_bin_set_receive_state (stream->receive_bin,
5936 /* must be called with the pc lock held */
5938 _generate_data_channel_id (GstWebRTCBin * webrtc)
5941 gint new_id = -1, max_channels = 0;
5943 if (webrtc->priv->sctp_transport) {
5944 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5947 if (max_channels <= 0) {
5948 max_channels = 65534;
5951 g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
5954 /* TODO: a better search algorithm */
5956 WebRTCDataChannel *channel;
5960 if (new_id < 0 || new_id >= max_channels) {
5961 /* exhausted id space */
5962 GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
5963 "data channel id (max %i)", max_channels);
5967 /* client must generate even ids, server must generate odd ids */
5968 if (new_id % 2 == !(!is_client))
5971 channel = _find_data_channel_for_id (webrtc, new_id);
5980 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
5981 const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
5984 const GstSDPMedia *local_media, *remote_media;
5985 GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
5986 TransportReceiveBin *receive;
5987 int local_port, remote_port;
5988 guint64 local_max_size, remote_max_size, max_size;
5992 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5995 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5998 local_setup = _get_dtls_setup_from_media (local_media);
5999 remote_setup = _get_dtls_setup_from_media (remote_media);
6000 new_setup = _get_final_setup (local_setup, remote_setup);
6001 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
6002 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6003 "Cannot intersect dtls setup for media %u", media_idx);
6007 /* data channel is always rtcp-muxed to avoid generating ICE candidates
6009 g_object_set (stream, "dtls-client",
6010 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
6012 local_port = _get_sctp_port_from_media (local_media);
6013 remote_port = _get_sctp_port_from_media (local_media);
6014 if (local_port == -1 || remote_port == -1) {
6015 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6016 "Could not find sctp port for media %u (local %i, remote %i)",
6017 media_idx, local_port, remote_port);
6021 if (0 == (local_max_size =
6022 _get_sctp_max_message_size_from_media (local_media)))
6023 local_max_size = G_MAXUINT64;
6024 if (0 == (remote_max_size =
6025 _get_sctp_max_message_size_from_media (remote_media)))
6026 remote_max_size = G_MAXUINT64;
6027 max_size = MIN (local_max_size, remote_max_size);
6029 webrtc->priv->sctp_transport->max_message_size = max_size;
6032 guint orig_local_port, orig_remote_port;
6034 /* XXX: sctpassociation warns if we are in the wrong state */
6035 g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
6036 &orig_local_port, NULL);
6038 if (orig_local_port != local_port)
6039 g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
6042 g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
6043 &orig_remote_port, NULL);
6044 if (orig_remote_port != remote_port)
6045 g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
6050 for (i = 0; i < webrtc->priv->data_channels->len; i++) {
6051 WebRTCDataChannel *channel;
6053 channel = g_ptr_array_index (webrtc->priv->data_channels, i);
6055 if (channel->parent.id == -1)
6056 channel->parent.id = _generate_data_channel_id (webrtc);
6057 if (channel->parent.id == -1)
6058 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
6059 ("%s", "Failed to generate an identifier for a data channel"), NULL);
6061 if (webrtc->priv->sctp_transport->association_established
6062 && !channel->parent.negotiated && !channel->opened) {
6063 webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
6064 webrtc_data_channel_start_negotiation (channel);
6069 stream->active = TRUE;
6071 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
6072 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
6076 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
6079 GstWebRTCKind kind = GPOINTER_TO_INT (data);
6083 if (p1->mline != -1)
6087 if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
6094 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
6099 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
6103 if (webrtc->rtpfunnel)
6106 webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
6107 gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
6108 gst_element_sync_state_with_parent (webrtc->rtpfunnel);
6110 srcpad = gst_element_get_static_pad (webrtc->rtpfunnel, "src");
6112 pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
6113 rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
6116 gst_pad_link (srcpad, rtp_sink);
6117 gst_object_unref (srcpad);
6118 gst_object_unref (rtp_sink);
6120 pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
6121 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
6122 if (webrtc->priv->netsim) {
6123 _insert_netsim_element_between (webrtc, GST_ELEMENT (webrtc->rtpbin), pad_name,
6124 GST_ELEMENT (stream->send_bin), "rtp_sink", session_id);
6127 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
6128 GST_ELEMENT (stream->send_bin), "rtp_sink"))
6129 g_warn_if_reached ();
6130 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
6140 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
6141 GstWebRTCSessionDescription * sdp, GError ** error)
6144 gboolean ret = FALSE;
6145 GStrv bundled = NULL;
6146 guint bundle_idx = 0;
6147 TransportStream *bundle_stream = NULL;
6149 /* FIXME: With some peers, it's possible we could have
6150 * multiple bundles to deal with, although I've never seen one yet */
6151 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
6152 if (!_parse_bundle (sdp->sdp, &bundled, error))
6157 if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
6158 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6159 "Bundle tag is %s but no media found matching", bundled[0]);
6163 bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
6164 _message_media_is_datachannel (sdp->sdp, bundle_idx));
6165 /* Mark the bundle stream as inactive to start. It will be set to TRUE
6166 * by any bundled mline that is active, and at the end we set the
6167 * receivebin to BLOCK if all mlines were inactive. */
6168 bundle_stream->active = FALSE;
6170 g_array_set_size (bundle_stream->ptmap, 0);
6171 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
6172 /* When bundling, we need to do this up front, or else RTX
6173 * parameters aren't set up properly for the bundled streams */
6174 _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
6176 ensure_rtx_hdr_ext (bundle_stream);
6178 _connect_rtpfunnel (webrtc, bundle_idx);
6181 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
6182 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
6183 TransportStream *stream;
6184 GstWebRTCRTPTransceiver *trans;
6185 guint transport_idx;
6187 /* skip rejected media */
6188 if (gst_sdp_media_get_port (media) == 0)
6192 transport_idx = bundle_idx;
6196 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
6198 stream = _get_or_create_transport_stream (webrtc, transport_idx,
6199 _message_media_is_datachannel (sdp->sdp, transport_idx));
6201 /* When bundling, these were all set up above, but when not
6202 * bundling we need to do it now */
6203 g_array_set_size (stream->ptmap, 0);
6204 _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
6205 ensure_rtx_hdr_ext (stream);
6209 webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
6211 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
6212 g_set_error (error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6213 "State mismatch. Could not find local transceiver by mline %u", i);
6216 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
6217 g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
6218 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
6220 /* No existing transceiver, find an unused one */
6222 if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
6223 kind = GST_WEBRTC_KIND_AUDIO;
6224 else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
6225 kind = GST_WEBRTC_KIND_VIDEO;
6227 GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
6228 GST_STR_NULL (gst_sdp_media_get_media (media)));
6230 trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
6231 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
6234 /* Still no transceiver? Create one */
6235 /* XXX: default to the advertised direction in the sdp for new
6236 * transceivers. The spec doesn't actually say what happens here, only
6237 * that calls to setDirection will change the value. Nothing about
6238 * a default value when the transceiver is created internally */
6240 WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
6241 _get_direction_from_media (media), i, kind, NULL);
6242 webrtc_transceiver_set_transport (t, stream);
6243 trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
6246 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
6247 trans, bundled, bundle_idx, error);
6248 if (error && *error)
6250 } else if (_message_media_is_datachannel (sdp->sdp, i)) {
6251 _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
6253 if (error && *error)
6256 GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
6261 if (bundle_stream && bundle_stream->active == FALSE) {
6262 /* No bundled mline marked the bundle as active, so block the receive bin, as
6263 * this bundle is completely inactive */
6264 GST_LOG_OBJECT (webrtc,
6265 "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
6266 transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
6267 RECEIVE_STATE_BLOCK);
6273 g_strfreev (bundled);
6279 transceivers_media_num_cmp (GstWebRTCBin * webrtc,
6280 GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
6285 return gst_sdp_message_medias_len (new->sdp) -
6286 gst_sdp_message_medias_len (previous->sdp);
6291 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
6296 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
6297 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
6298 GstWebRTCRTPTransceiver *rtp_trans;
6299 WebRTCTransceiver *trans;
6301 rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
6302 /* only look for matching mid */
6303 if (rtp_trans == NULL)
6306 trans = WEBRTC_TRANSCEIVER (rtp_trans);
6308 /* We only validate the locked mlines for now */
6309 if (!trans->mline_locked)
6312 if (rtp_trans->mline != i) {
6313 g_set_error (error, GST_WEBRTC_ERROR,
6314 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
6315 "m-line with mid %s is at position %d, but was locked to %d, "
6316 "rejecting", rtp_trans->mid, i, rtp_trans->mline);
6320 if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
6321 if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
6322 rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
6323 g_set_error (error, GST_WEBRTC_ERROR,
6324 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
6325 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
6326 "%s media", i, GST_OBJECT_NAME (rtp_trans),
6327 gst_webrtc_kind_to_string (rtp_trans->kind),
6328 gst_sdp_media_get_media (media));
6332 if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
6333 rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
6334 g_set_error (error, GST_WEBRTC_ERROR,
6335 GST_WEBRTC_ERROR_INTERNAL_FAILURE,
6336 "m-line %d with transceiver <%s> was locked to %s, but SDP has "
6337 "%s media", i, GST_OBJECT_NAME (rtp_trans),
6338 gst_webrtc_kind_to_string (rtp_trans->kind),
6339 gst_sdp_media_get_media (media));
6349 struct set_description
6352 GstWebRTCSessionDescription *sdp;
6355 static GstWebRTCSessionDescription *
6356 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
6357 GstWebRTCSDPType type)
6360 case GST_WEBRTC_SDP_TYPE_OFFER:
6361 case GST_WEBRTC_SDP_TYPE_PRANSWER:
6362 case GST_WEBRTC_SDP_TYPE_ANSWER:
6363 if (source == SDP_LOCAL) {
6364 return webrtc->current_local_description;
6366 return webrtc->current_remote_description;
6368 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
6371 /* other values mean memory corruption/uninitialized! */
6372 g_assert_not_reached ();
6379 static GstWebRTCSessionDescription *
6380 get_last_generated_description (GstWebRTCBin * webrtc, SDPSource source,
6381 GstWebRTCSDPType type)
6384 case GST_WEBRTC_SDP_TYPE_OFFER:
6385 if (source == SDP_REMOTE)
6386 return webrtc->priv->last_generated_answer;
6388 return webrtc->priv->last_generated_offer;
6390 case GST_WEBRTC_SDP_TYPE_PRANSWER:
6391 case GST_WEBRTC_SDP_TYPE_ANSWER:
6392 if (source == SDP_LOCAL)
6393 return webrtc->priv->last_generated_answer;
6395 return webrtc->priv->last_generated_offer;
6396 case GST_WEBRTC_SDP_TYPE_ROLLBACK:
6399 /* other values mean memory corruption/uninitialized! */
6400 g_assert_not_reached ();
6408 /* http://w3c.github.io/webrtc-pc/#set-description */
6409 static GstStructure *
6410 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
6412 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
6413 gboolean signalling_state_changed = FALSE;
6414 GError *error = NULL;
6415 GStrv bundled = NULL;
6416 guint bundle_idx = 0;
6420 const gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6421 webrtc->signaling_state);
6422 const gchar *type_str =
6423 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
6424 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
6425 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
6426 _sdp_source_to_string (sd->source), type_str, state);
6427 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
6431 if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
6434 if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
6435 if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
6439 if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
6440 g_set_error (&error, GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6441 "Bundle tag is %s but no matching media found", bundled[0]);
6446 if (transceivers_media_num_cmp (webrtc,
6447 get_previous_description (webrtc, sd->source, sd->sdp->type),
6449 g_set_error_literal (&error, GST_WEBRTC_ERROR,
6450 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6451 "m=lines removed from the SDP. Processing a completely new connection "
6452 "is not currently supported.");
6456 if ((sd->sdp->type == GST_WEBRTC_SDP_TYPE_PRANSWER ||
6457 sd->sdp->type == GST_WEBRTC_SDP_TYPE_ANSWER) &&
6458 transceivers_media_num_cmp (webrtc,
6459 get_last_generated_description (webrtc, sd->source, sd->sdp->type),
6461 g_set_error_literal (&error, GST_WEBRTC_ERROR,
6462 GST_WEBRTC_ERROR_SDP_SYNTAX_ERROR,
6463 "Answer doesn't have the same number of m-lines as the offer.");
6467 if (!check_locked_mlines (webrtc, sd->sdp, &error))
6470 switch (sd->sdp->type) {
6471 case GST_WEBRTC_SDP_TYPE_OFFER:{
6472 if (sd->source == SDP_LOCAL) {
6473 if (webrtc->pending_local_description)
6474 gst_webrtc_session_description_free
6475 (webrtc->pending_local_description);
6476 webrtc->pending_local_description =
6477 gst_webrtc_session_description_copy (sd->sdp);
6478 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
6480 if (webrtc->pending_remote_description)
6481 gst_webrtc_session_description_free
6482 (webrtc->pending_remote_description);
6483 webrtc->pending_remote_description =
6484 gst_webrtc_session_description_copy (sd->sdp);
6485 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
6489 case GST_WEBRTC_SDP_TYPE_ANSWER:{
6490 if (sd->source == SDP_LOCAL) {
6491 if (webrtc->current_local_description)
6492 gst_webrtc_session_description_free
6493 (webrtc->current_local_description);
6494 webrtc->current_local_description =
6495 gst_webrtc_session_description_copy (sd->sdp);
6497 if (webrtc->current_remote_description)
6498 gst_webrtc_session_description_free
6499 (webrtc->current_remote_description);
6500 webrtc->current_remote_description = webrtc->pending_remote_description;
6501 webrtc->pending_remote_description = NULL;
6503 if (webrtc->current_remote_description)
6504 gst_webrtc_session_description_free
6505 (webrtc->current_remote_description);
6506 webrtc->current_remote_description =
6507 gst_webrtc_session_description_copy (sd->sdp);
6509 if (webrtc->current_local_description)
6510 gst_webrtc_session_description_free
6511 (webrtc->current_local_description);
6512 webrtc->current_local_description = webrtc->pending_local_description;
6513 webrtc->pending_local_description = NULL;
6516 if (webrtc->pending_local_description)
6517 gst_webrtc_session_description_free (webrtc->pending_local_description);
6518 webrtc->pending_local_description = NULL;
6520 if (webrtc->pending_remote_description)
6521 gst_webrtc_session_description_free
6522 (webrtc->pending_remote_description);
6523 webrtc->pending_remote_description = NULL;
6525 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
6528 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
6529 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
6530 if (sd->source == SDP_LOCAL) {
6531 if (webrtc->pending_local_description)
6532 gst_webrtc_session_description_free
6533 (webrtc->pending_local_description);
6534 webrtc->pending_local_description = NULL;
6536 if (webrtc->pending_remote_description)
6537 gst_webrtc_session_description_free
6538 (webrtc->pending_remote_description);
6539 webrtc->pending_remote_description = NULL;
6542 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
6545 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
6546 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
6547 if (sd->source == SDP_LOCAL) {
6548 if (webrtc->pending_local_description)
6549 gst_webrtc_session_description_free
6550 (webrtc->pending_local_description);
6551 webrtc->pending_local_description =
6552 gst_webrtc_session_description_copy (sd->sdp);
6554 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
6556 if (webrtc->pending_remote_description)
6557 gst_webrtc_session_description_free
6558 (webrtc->pending_remote_description);
6559 webrtc->pending_remote_description =
6560 gst_webrtc_session_description_copy (sd->sdp);
6562 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
6568 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
6570 * If the mid value of an RTCRtpTransceiver was set to a non-null value
6571 * by the RTCSessionDescription that is being rolled back, set the mid
6572 * value of that transceiver to null, as described by [JSEP]
6573 * (section 4.1.7.2.).
6574 * If an RTCRtpTransceiver was created by applying the
6575 * RTCSessionDescription that is being rolled back, and a track has not
6576 * been attached to it via addTrack, remove that transceiver from
6577 * connection's set of transceivers, as described by [JSEP]
6578 * (section 4.1.7.2.).
6579 * Restore the value of connection's [[ sctpTransport]] internal slot
6580 * to its value at the last stable signaling state.
6584 if (webrtc->signaling_state != new_signaling_state) {
6585 webrtc->signaling_state = new_signaling_state;
6586 signalling_state_changed = TRUE;
6590 gboolean ice_controller = FALSE;
6592 /* get the current value so we don't change ice controller from TRUE to
6593 * FALSE on renegotiation or once set to TRUE for the initial local offer */
6594 ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
6596 /* we control ice negotiation if we send the initial offer */
6598 new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
6599 && webrtc->current_remote_description == NULL;
6600 /* or, if the remote is an ice-lite peer */
6601 ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
6602 && webrtc->current_remote_description
6603 && _message_has_attribute_key (webrtc->current_remote_description->sdp,
6606 GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
6607 ice_controller ? "true" : "false");
6608 gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
6611 if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6614 /* media modifications */
6615 if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
6618 for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
6619 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
6620 GstWebRTCRTPTransceiverDirection new_dir;
6622 const GstSDPMedia *media;
6624 if (!pad->received_caps) {
6625 GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
6631 GST_LOG_OBJECT (pad, "doesn't have a transceiver");
6636 if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
6637 GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
6642 media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
6643 /* skip rejected media */
6644 if (gst_sdp_media_get_port (media) == 0) {
6645 /* FIXME: arrange for an appropriate flow return */
6646 GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for "
6647 "a more correct flow return.");
6652 new_dir = pad->trans->direction;
6653 if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
6654 new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
6655 GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
6656 "data at the moment. Not connecting input stream yet", pad->trans);
6661 GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
6662 "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
6663 pad->trans, pad->received_caps);
6664 _connect_input_stream (webrtc, pad);
6665 gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
6669 gst_object_unref (old->data);
6670 webrtc->priv->pending_sink_transceivers =
6671 g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
6675 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
6676 const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
6678 TransportStream *item;
6679 guint rtp_session_id = bundled ? bundle_idx : i;
6682 _get_or_create_transport_stream (webrtc, rtp_session_id,
6683 _message_media_is_datachannel (sd->sdp->sdp, rtp_session_id));
6685 if (sd->source == SDP_REMOTE) {
6688 for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
6689 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
6691 if (g_strcmp0 (attr->key, "ssrc") == 0) {
6692 GStrv split = g_strsplit (attr->value, " ", 0);
6695 if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
6696 && g_str_has_prefix (split[1], "cname:")) {
6697 if (!find_mid_ssrc_for_ssrc (webrtc,
6698 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY,
6699 rtp_session_id, ssrc))
6700 transport_stream_add_ssrc_map_item (item,
6701 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, ssrc, i);
6708 if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
6709 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6711 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
6712 item->stream, ufrag, pwd);
6715 } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
6716 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
6718 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
6719 item->stream, ufrag, pwd);
6725 if (sd->source == SDP_LOCAL) {
6726 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
6727 IceStreamItem *item =
6728 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
6730 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
6734 /* Add any pending trickle ICE candidates if we have both offer and answer */
6735 if (webrtc->current_local_description && webrtc->current_remote_description) {
6738 GstWebRTCSessionDescription *remote_sdp =
6739 webrtc->current_remote_description;
6741 /* Add any remote ICE candidates from the remote description to
6742 * support non-trickle peers first */
6743 for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
6744 const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
6745 _add_ice_candidates_from_sdp (webrtc, i, media);
6749 for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
6750 IceCandidateItem *item =
6751 &g_array_index (webrtc->priv->pending_remote_ice_candidates,
6752 IceCandidateItem, i);
6754 _add_ice_candidate (webrtc, item, TRUE);
6756 g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
6757 ICE_UNLOCK (webrtc);
6761 * If connection's signaling state changed above, fire an event named
6762 * signalingstatechange at connection.
6764 if (signalling_state_changed) {
6765 const gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6766 webrtc->signaling_state);
6767 const gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
6768 new_signaling_state);
6769 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
6772 g_object_notify (G_OBJECT (webrtc), "signaling-state");
6776 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
6777 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
6779 /* If connection's signaling state is now stable, update the
6780 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
6781 * was true both before and after this update, queue a task to check
6782 * connection's [[needNegotiation]] slot and, if still true, fire a
6783 * simple event named negotiationneeded at connection.*/
6784 _update_need_negotiation (webrtc);
6785 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
6786 _check_need_negotiation_task (webrtc, NULL);
6791 g_strfreev (bundled);
6794 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6795 "error", G_TYPE_ERROR, error, NULL);
6796 GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
6797 g_clear_error (&error);
6805 _free_set_description_data (struct set_description *sd)
6808 gst_webrtc_session_description_free (sd->sdp);
6813 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
6814 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
6816 struct set_description *sd;
6818 if (remote_sdp == NULL)
6820 if (remote_sdp->sdp == NULL)
6823 sd = g_new0 (struct set_description, 1);
6824 sd->source = SDP_REMOTE;
6825 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
6827 if (!gst_webrtc_bin_enqueue_task (webrtc,
6828 (GstWebRTCBinFunc) _set_description_task, sd,
6829 (GDestroyNotify) _free_set_description_data, promise)) {
6831 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6832 "Could not set remote description. webrtcbin is closed.");
6833 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6834 "error", G_TYPE_ERROR, error, NULL);
6836 gst_promise_reply (promise, s);
6838 g_clear_error (&error);
6845 gst_promise_reply (promise, NULL);
6846 g_return_if_reached ();
6851 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
6852 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
6854 struct set_description *sd;
6856 if (local_sdp == NULL)
6858 if (local_sdp->sdp == NULL)
6861 sd = g_new0 (struct set_description, 1);
6862 sd->source = SDP_LOCAL;
6863 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
6865 if (!gst_webrtc_bin_enqueue_task (webrtc,
6866 (GstWebRTCBinFunc) _set_description_task, sd,
6867 (GDestroyNotify) _free_set_description_data, promise)) {
6869 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
6870 "Could not set local description. webrtcbin is closed");
6871 GstStructure *s = gst_structure_new ("application/x-gst-promise",
6872 "error", G_TYPE_ERROR, error, NULL);
6874 gst_promise_reply (promise, s);
6876 g_clear_error (&error);
6883 gst_promise_reply (promise, NULL);
6884 g_return_if_reached ();
6888 static GstStructure *
6889 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
6891 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
6892 IceCandidateItem new;
6893 new.mlineindex = item->mlineindex;
6894 new.candidate = g_steal_pointer (&item->candidate);
6897 g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
6898 ICE_UNLOCK (webrtc);
6900 _add_ice_candidate (webrtc, item, FALSE);
6907 _free_ice_candidate_item (IceCandidateItem * item)
6909 _clear_ice_candidate_item (item);
6914 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
6917 IceCandidateItem *item;
6919 item = g_new0 (IceCandidateItem, 1);
6920 item->mlineindex = mline;
6921 if (attr && attr[0] != 0) {
6922 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
6923 item->candidate = g_strdup (attr);
6924 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
6925 item->candidate = g_strdup_printf ("a=%s", attr);
6927 gst_webrtc_bin_enqueue_task (webrtc,
6928 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
6929 (GDestroyNotify) _free_ice_candidate_item, NULL);
6932 static GstStructure *
6933 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
6939 if (webrtc->priv->pending_local_ice_candidates->len == 0) {
6940 ICE_UNLOCK (webrtc);
6941 GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
6942 return NULL; /* Nothing to process */
6944 /* Take the array so we can process it all and free it later
6945 * without holding the lock
6946 * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
6948 items = webrtc->priv->pending_local_ice_candidates;
6949 /* Replace with a new array */
6950 webrtc->priv->pending_local_ice_candidates =
6951 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
6952 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
6953 (GDestroyNotify) _clear_ice_candidate_item);
6954 ICE_UNLOCK (webrtc);
6956 for (i = 0; i < items->len; i++) {
6957 IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
6958 const gchar *cand = item->candidate;
6960 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
6961 /* stripping away "a=" */
6965 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
6966 item->mlineindex, cand);
6968 /* First, merge this ice candidate into the appropriate mline
6969 * in the local-description SDP.
6970 * Second, emit the on-ice-candidate signal for the app.
6972 * FIXME: This ICE candidate should be stored somewhere with
6973 * the associated mid and also merged back into any subsequent
6974 * local descriptions on renegotiation */
6975 if (webrtc->current_local_description)
6976 _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
6977 item->mlineindex, cand);
6978 if (webrtc->pending_local_description)
6979 _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
6980 item->mlineindex, cand);
6983 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
6984 0, item->mlineindex, cand);
6988 g_array_free (items, TRUE);
6990 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
6991 if (webrtc->pending_ice_gathering_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
6992 _update_and_notify_ice_gathering_state (webrtc, GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE);
6993 webrtc->pending_ice_gathering_state = GST_WEBRTC_ICE_GATHERING_STATE_NEW;
7001 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
7002 gchar * candidate, GstWebRTCBin * webrtc)
7004 IceCandidateItem item;
7005 gboolean queue_task = FALSE;
7007 item.mlineindex = session_id;
7008 item.candidate = g_strdup (candidate);
7011 g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
7013 /* Let the first pending candidate queue a task each time, which will
7014 * handle any that arrive between now and when the task runs */
7015 if (webrtc->priv->pending_local_ice_candidates->len == 1)
7017 ICE_UNLOCK (webrtc);
7020 GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
7021 gst_webrtc_bin_enqueue_task (webrtc,
7022 (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
7029 GstPromise *promise;
7033 _free_get_stats (struct get_stats *stats)
7036 gst_object_unref (stats->pad);
7038 gst_promise_unref (stats->promise);
7042 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
7043 static GstStructure *
7044 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
7046 /* Our selector is the pad,
7047 * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
7050 return gst_webrtc_bin_create_stats (webrtc, stats->pad);
7054 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
7055 GstPromise * promise)
7057 struct get_stats *stats;
7059 g_return_if_fail (promise != NULL);
7060 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
7062 stats = g_new0 (struct get_stats, 1);
7063 stats->promise = gst_promise_ref (promise);
7064 /* FIXME: check that pad exists in element */
7066 stats->pad = gst_object_ref (pad);
7068 if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
7069 stats, (GDestroyNotify) _free_get_stats, promise)) {
7071 g_error_new (GST_WEBRTC_ERROR, GST_WEBRTC_ERROR_INVALID_STATE,
7072 "Could not retrieve statistics. webrtcbin is closed.");
7073 GstStructure *s = gst_structure_new ("application/x-gst-promise",
7074 "error", G_TYPE_ERROR, error, NULL);
7076 gst_promise_reply (promise, s);
7078 g_clear_error (&error);
7082 static GstWebRTCRTPTransceiver *
7083 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
7084 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
7086 WebRTCTransceiver *trans;
7088 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
7094 _create_webrtc_transceiver (webrtc, direction, -1,
7095 webrtc_kind_from_caps (caps), caps);
7096 GST_LOG_OBJECT (webrtc,
7097 "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
7101 return gst_object_ref (trans);
7105 _deref_and_unref (GstObject ** object)
7107 gst_clear_object (object);
7111 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
7113 GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
7118 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
7120 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
7121 GstWebRTCRTPTransceiver *trans =
7122 g_ptr_array_index (webrtc->priv->transceivers, i);
7123 gst_object_ref (trans);
7124 g_array_append_val (arr, trans);
7131 static GstWebRTCRTPTransceiver *
7132 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
7134 GstWebRTCRTPTransceiver *trans = NULL;
7138 if (idx >= webrtc->priv->transceivers->len) {
7139 GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
7143 trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
7144 gst_object_ref (trans);
7152 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
7156 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
7157 g_return_val_if_fail (uri != NULL, FALSE);
7159 GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
7162 ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
7169 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
7171 GstPad *gpad = GST_PAD_CAST (user_data);
7173 GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
7174 gst_pad_store_sticky_event (gpad, *event);
7179 static WebRTCDataChannel *
7180 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
7181 GstStructure * init_params)
7184 gint max_packet_lifetime;
7185 gint max_retransmits;
7186 const gchar *protocol;
7187 gboolean negotiated;
7189 GstWebRTCPriorityType priority;
7190 WebRTCDataChannel *ret;
7191 gint max_channels = 65534;
7193 g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
7194 g_return_val_if_fail (label != NULL, NULL);
7195 g_return_val_if_fail (strlen (label) <= 65535, NULL);
7196 #ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
7197 g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
7201 || !gst_structure_get_boolean (init_params, "ordered", &ordered))
7204 || !gst_structure_get_int (init_params, "max-packet-lifetime",
7205 &max_packet_lifetime))
7206 max_packet_lifetime = -1;
7208 || !gst_structure_get_int (init_params, "max-retransmits",
7210 max_retransmits = -1;
7211 /* both retransmits and lifetime cannot be set */
7212 g_return_val_if_fail ((max_packet_lifetime == -1)
7213 || (max_retransmits == -1), NULL);
7216 || !(protocol = gst_structure_get_string (init_params, "protocol")))
7218 g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
7221 || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
7223 if (!negotiated || !init_params
7224 || !gst_structure_get_int (init_params, "id", &id))
7227 g_return_val_if_fail (id != -1, NULL);
7228 g_return_val_if_fail (id < 65535, NULL);
7231 || !gst_structure_get_enum (init_params, "priority",
7232 GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
7233 priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
7235 /* FIXME: clamp max-retransmits and max-packet-lifetime */
7237 if (webrtc->priv->sctp_transport) {
7238 /* Let transport be the connection's [[SctpTransport]] slot.
7240 * If the [[DataChannelId]] slot is not null, transport is in
7241 * connected state and [[DataChannelId]] is greater or equal to the
7242 * transport's [[MaxChannels]] slot, throw an OperationError.
7244 g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
7247 if (max_channels <= 0) {
7248 max_channels = 65534;
7251 g_return_val_if_fail (id <= max_channels, NULL);
7254 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
7255 !_have_sctp_elements (webrtc))
7260 /* check if the id has been used already */
7262 WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
7264 GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
7265 ("Attempting to add a data channel with a duplicate ID: %i", id),
7271 } else if (webrtc->current_local_description
7272 && webrtc->current_remote_description && webrtc->priv->sctp_transport
7273 && webrtc->priv->sctp_transport->transport) {
7274 /* else we can only generate an id if we're configured already. The other
7275 * case for generating an id is on sdp setting */
7276 id = _generate_data_channel_id (webrtc);
7278 GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
7279 ("%s", "Failed to generate an identifier for a data channel"), NULL);
7286 ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
7287 "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
7288 "max-retransmits", max_retransmits, "protocol", protocol,
7289 "negotiated", negotiated, "id", id, "priority", priority, NULL);
7297 g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL], 0,
7300 gst_bin_add (GST_BIN (webrtc), ret->src_bin);
7301 gst_bin_add (GST_BIN (webrtc), ret->sink_bin);
7303 gst_element_sync_state_with_parent (ret->src_bin);
7304 gst_element_sync_state_with_parent (ret->sink_bin);
7306 ret = gst_object_ref (ret);
7307 webrtc_data_channel_set_webrtcbin (ret, webrtc);
7308 g_ptr_array_add (webrtc->priv->data_channels, ret);
7311 gst_webrtc_bin_update_sctp_priority (webrtc);
7312 webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
7313 if (webrtc->priv->sctp_transport &&
7314 webrtc->priv->sctp_transport->association_established
7315 && !ret->parent.negotiated) {
7316 webrtc_data_channel_start_negotiation (ret);
7318 _update_need_negotiation (webrtc);
7325 /* === rtpbin signal implementations === */
7328 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
7329 GstWebRTCBin * webrtc)
7331 gchar *new_pad_name = NULL;
7333 new_pad_name = gst_pad_get_name (new_pad);
7334 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
7335 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
7336 guint32 session_id = 0, ssrc = 0, pt = 0;
7337 SsrcMapItem *mid_entry;
7338 GstWebRTCRTPTransceiver *rtp_trans = NULL;
7339 WebRTCTransceiver *trans;
7340 TransportStream *stream;
7341 GstWebRTCBinPad *pad;
7344 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
7346 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
7350 media_idx = session_id;
7353 stream = _find_transport_for_session (webrtc, session_id);
7355 g_warn_if_reached ();
7358 find_mid_ssrc_for_ssrc (webrtc,
7359 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc);
7362 if (mid_entry->mid) {
7363 /* Can't use the mid_entry if the mid doesn't exist */
7364 rtp_trans = _find_transceiver_for_mid (webrtc, mid_entry->mid);
7366 g_assert_cmpint (rtp_trans->mline, ==, mid_entry->media_idx);
7370 if (mid_entry->media_idx != -1)
7371 media_idx = mid_entry->media_idx;
7373 GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
7374 /* TODO: connect up to fakesink and reconnect later when this information
7375 * is known from RTCP SDES or RTP Header extension
7380 rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
7382 g_warn_if_reached ();
7383 trans = WEBRTC_TRANSCEIVER (rtp_trans);
7384 g_assert (trans->stream == stream);
7386 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
7387 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
7388 " for rtpbin pad name %s", pad, new_pad_name);
7389 if (!_remove_pending_pad (webrtc, pad)) {
7390 /* assumption here is that rtpbin doesn't duplicate pads and that if
7391 * there is no pending pad, this is a duplicate stream for e.g. simulcast
7393 gst_clear_object (&pad);
7395 _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT,
7397 GST_TRACE_OBJECT (webrtc,
7398 "duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %"
7399 GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name);
7400 gst_object_ref_sink (pad);
7404 g_warn_if_reached ();
7405 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
7407 if (webrtc->priv->running)
7408 gst_pad_set_active (GST_PAD (pad), TRUE);
7412 gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
7413 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
7415 gst_object_unref (pad);
7417 g_free (new_pad_name);
7420 /* only used for the receiving streams */
7422 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
7423 GstWebRTCBin * webrtc)
7425 TransportStream *stream;
7428 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
7432 stream = _find_transport_for_session (webrtc, session_id);
7434 goto unknown_session;
7436 if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
7439 GST_DEBUG_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
7440 "session %d", ret, pt, session_id);
7448 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
7454 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
7455 GstWebRTCBin * webrtc)
7457 TransportStream *stream;
7458 GstElement *ret, *rtx;
7461 GstElement *aux_sender = NULL;
7463 stream = _find_transport_for_session (webrtc, session_id);
7465 /* a rtp session without a stream is a webrtcbin bug */
7466 g_warn_if_reached ();
7470 if (stream->rtxsend) {
7471 GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
7472 g_warn_if_reached ();
7476 GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
7477 "stream %" GST_PTR_FORMAT, session_id, stream);
7479 ret = gst_bin_new (NULL);
7480 rtx = gst_element_factory_make ("rtprtxsend", NULL);
7481 /* XXX: allow control from outside? */
7482 g_object_set (rtx, "max-size-packets", 500, NULL);
7484 if (!gst_bin_add (GST_BIN (ret), rtx))
7485 g_warn_if_reached ();
7486 ensure_rtx_hdr_ext (stream);
7488 stream->rtxsend = gst_object_ref (rtx);
7489 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7491 name = g_strdup_printf ("src_%u", session_id);
7492 pad = gst_element_get_static_pad (rtx, "src");
7495 g_signal_emit (webrtc, gst_webrtc_bin_signals[REQUEST_AUX_SENDER], 0,
7496 stream->transport, &aux_sender);
7498 GstPadLinkReturn link_res;
7499 GstPad *sinkpad = gst_element_get_static_pad (aux_sender, "sink");
7500 GstPad *srcpad = gst_element_get_static_pad (aux_sender, "src");
7502 gst_object_ref_sink (aux_sender);
7504 if (!sinkpad || !srcpad) {
7505 GST_ERROR_OBJECT (webrtc,
7506 "Invalid pads for the aux sender %" GST_PTR_FORMAT
7507 ". Skipping it.", aux_sender);
7511 if (!gst_bin_add (GST_BIN (ret), aux_sender)) {
7512 GST_ERROR_OBJECT (webrtc,
7513 "Could not add aux sender %" GST_PTR_FORMAT, aux_sender);
7517 link_res = gst_pad_link (pad, sinkpad);
7518 if (link_res != GST_PAD_LINK_OK) {
7519 GST_ERROR_OBJECT (webrtc,
7520 "Could not link aux sender %" GST_PTR_FORMAT " %s", aux_sender,
7521 gst_pad_link_get_name (link_res));
7525 gst_clear_object (&pad);
7526 pad = gst_object_ref (srcpad);
7529 if (pad != srcpad) {
7530 /* Failed using the provided aux sender */
7531 if (gst_object_has_as_parent (GST_OBJECT (aux_sender), GST_OBJECT (ret))) {
7532 gst_bin_remove (GST_BIN (ret), aux_sender);
7535 gst_clear_object (&aux_sender);
7536 gst_clear_object (&srcpad);
7537 gst_clear_object (&sinkpad);
7540 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7541 g_warn_if_reached ();
7542 gst_clear_object (&pad);
7543 g_clear_pointer (&name, g_free);
7545 name = g_strdup_printf ("sink_%u", session_id);
7546 pad = gst_element_get_static_pad (rtx, "sink");
7547 if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7548 g_warn_if_reached ();
7549 gst_clear_object (&pad);
7550 g_clear_pointer (&name, g_free);
7556 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
7557 GstWebRTCBin * webrtc)
7559 TransportStream *stream;
7560 GstPad *pad, *ghost;
7564 stream = _find_transport_for_session (webrtc, session_id);
7566 /* no transport stream before the session has been created is a webrtcbin
7567 * programming error! */
7568 g_warn_if_reached ();
7572 if (stream->rtxreceive) {
7573 GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
7574 g_warn_if_reached ();
7578 if (stream->reddec) {
7579 GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
7580 g_warn_if_reached ();
7584 GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
7585 "stream %" GST_PTR_FORMAT, session_id, stream);
7587 ret = gst_bin_new (NULL);
7589 stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
7590 gst_object_ref (stream->rtxreceive);
7591 if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
7592 g_warn_if_reached ();
7594 ensure_rtx_hdr_ext (stream);
7596 stream->reddec = gst_element_factory_make ("rtpreddec", NULL);
7597 gst_object_ref (stream->reddec);
7598 if (!gst_bin_add (GST_BIN (ret), stream->reddec))
7599 g_warn_if_reached ();
7601 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7603 if (!gst_element_link (stream->rtxreceive, stream->reddec))
7604 g_warn_if_reached ();
7606 name = g_strdup_printf ("sink_%u", session_id);
7607 pad = gst_element_get_static_pad (stream->rtxreceive, "sink");
7608 ghost = gst_ghost_pad_new (name, pad);
7609 g_clear_pointer (&name, g_free);
7610 gst_clear_object (&pad);
7611 if (!gst_element_add_pad (ret, ghost))
7612 g_warn_if_reached ();
7614 name = g_strdup_printf ("src_%u", session_id);
7615 pad = gst_element_get_static_pad (stream->reddec, "src");
7616 ghost = gst_ghost_pad_new (name, pad);
7617 g_clear_pointer (&name, g_free);
7618 gst_clear_object (&pad);
7619 if (!gst_element_add_pad (ret, ghost))
7620 g_warn_if_reached ();
7626 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
7627 guint ssrc, guint pt, GstWebRTCBin * webrtc)
7629 TransportStream *stream;
7630 GstElement *ret = NULL;
7631 GObject *internal_storage;
7633 stream = _find_transport_for_session (webrtc, session_id);
7635 /* a rtp session without a stream is a webrtcbin bug */
7636 g_warn_if_reached ();
7640 /* TODO: for now, we only support ulpfec, but once we support
7641 * more algorithms, if the remote may use more than one algorithm,
7642 * we will want to do the following:
7644 * + Return a bin here, with the relevant FEC decoders plugged in
7645 * and their payload type set to 0
7647 GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
7648 "stream %" GST_PTR_FORMAT, pt, session_id, stream);
7650 ret = gst_element_factory_make ("rtpulpfecdec", NULL);
7652 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
7655 g_object_set (ret, "storage", internal_storage, NULL);
7656 g_clear_object (&internal_storage);
7658 g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
7659 GINT_TO_POINTER (pt));
7662 stream->fecdecs = g_list_prepend (stream->fecdecs, gst_object_ref (ret));
7663 _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7670 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7671 GstWebRTCBin * webrtc)
7673 GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
7676 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7681 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7682 GstWebRTCBin * webrtc)
7684 GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
7687 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7692 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7693 GstWebRTCBin * webrtc)
7695 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
7699 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7704 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7705 GstWebRTCBin * webrtc)
7707 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
7713 find_or_add_ssrc_map_item (webrtc,
7714 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc, -1);
7719 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7720 GstWebRTCBin * webrtc)
7722 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
7726 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
7727 GstWebRTCBin * webrtc)
7729 GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
7733 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
7734 GstWebRTCBin * webrtc)
7738 GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
7740 g_signal_emit_by_name (rtpbin, "get-internal-session", session_id, &session);
7744 g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7748 g_object_get (source, "sdes", &sdes, NULL);
7750 /* TODO: when the sdes contains the mid, use that to correlate streams
7752 GST_DEBUG_OBJECT (webrtc, "session %u ssrc %u sdes %" GST_PTR_FORMAT,
7753 session_id, ssrc, sdes);
7755 gst_clear_structure (&sdes);
7756 gst_clear_object (&source);
7758 g_clear_object (&session);
7763 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
7764 GstWebRTCBin * webrtc)
7766 GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
7769 #ifdef TIZEN_FEATURE_WEBRTC_SSRC_TIMEOUT_NOTIFICATION
7771 post_ssrc_timeout_error_msg (GstWebRTCBin *webrtc, guint ssrc)
7773 GError *err = g_error_new (GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_TOO_LAZY, "RTP session ssrc timeout happened");
7774 gchar *debug = g_strdup_printf("timeout on ssrc %u", ssrc);
7775 GstMessage *msg = gst_message_new_error (GST_OBJECT_CAST (webrtc), err, debug);
7779 gst_element_post_message (GST_ELEMENT_CAST (webrtc), msg);
7784 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7785 GstWebRTCBin * webrtc)
7787 GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
7790 remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7791 #ifdef TIZEN_FEATURE_WEBRTC_SSRC_TIMEOUT_NOTIFICATION
7792 post_ssrc_timeout_error_msg (webrtc, ssrc);
7798 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7799 GstWebRTCBin * webrtc)
7803 GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
7807 mid = find_mid_ssrc_for_ssrc (webrtc,
7808 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, session_id, ssrc);
7810 TransportStream *stream = _find_transport_for_session (webrtc, session_id);
7811 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
7814 transport_stream_add_ssrc_map_item (stream,
7815 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, ssrc, -1);
7816 } else if (mid->mid) {
7817 /* XXX: when peers support the sdes rtcp item, use this to send the mid rtcp
7818 * sdes item. Requires being able to set the sdes on the rtpsource. */
7822 g_signal_emit_by_name (rtpbin, "get-internal-session", session_id,
7827 g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7830 const char *sdes_field_name;
7832 g_object_get (source, "sdes", &sdes, NULL);
7833 GST_WARNING_OBJECT (webrtc, "session %u ssrc %u retrieve sdes %"
7834 GST_PTR_FORMAT, session_id, ssrc, sdes);
7835 sdes_field_name = gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_MID);
7836 g_assert (sdes_field_name);
7837 gst_structure_set (sdes, sdes_field_name, G_TYPE_STRING, mid->mid,
7841 gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_RTP_STREAM_ID);
7842 g_assert (sdes_field_name);
7843 gst_structure_set (sdes, sdes_field_name, mid->rid, NULL);
7844 // TODO: repaired-rtp-stream-id
7846 // TODO: writable sdes?
7847 g_object_set (source, "sdes", sdes, NULL);
7848 GST_INFO_OBJECT (webrtc,
7849 "session %u ssrc %u set sdes %" GST_PTR_FORMAT, session_id, ssrc,
7852 gst_clear_structure (&sdes);
7853 gst_clear_object (&source);
7855 g_clear_object (&session);
7863 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7864 GstWebRTCBin * webrtc)
7866 GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
7872 GstWebRTCBin *webrtc;
7873 GstElement *jitterbuffer;
7874 TransportStream *stream;
7879 jitter_buffer_set_retransmission (SsrcMapItem * item,
7880 const struct new_jb_args *data)
7882 GstWebRTCRTPTransceiver *trans;
7885 if (item->media_idx == -1)
7888 trans = _find_transceiver_for_mline (data->webrtc, item->media_idx);
7890 g_warn_if_reached ();
7894 do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
7895 /* We don't set do-retransmission on rtpbin as we want per-session control */
7896 GST_LOG_OBJECT (data->webrtc, "setting do-nack=%s for transceiver %"
7897 GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
7898 " rtp session %u ssrc %u", do_nack ? "true" : "false", trans,
7899 data->stream, data->stream->session_id, data->ssrc);
7900 g_object_set (data->jitterbuffer, "do-retransmission", do_nack, NULL);
7902 g_weak_ref_set (&item->rtpjitterbuffer, data->jitterbuffer);
7908 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
7909 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
7911 TransportStream *stream;
7912 struct new_jb_args d = { 0, };
7915 GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
7916 "session %u ssrc %u", jitterbuffer, session_id, ssrc);
7918 if (!(stream = _find_transport_for_session (webrtc, session_id))) {
7919 g_warn_if_reached ();
7924 d.jitterbuffer = jitterbuffer;
7927 transport_stream_filter_ssrc_map_item (stream, &d,
7928 (FindSsrcMapFunc) jitter_buffer_set_retransmission);
7935 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
7936 guint session_id, GstWebRTCBin * webrtc)
7938 guint64 latency = webrtc->priv->jb_latency;
7940 /* Add an extra 50 ms for safey */
7941 latency += RTPSTORAGE_EXTRA_TIME;
7942 latency *= GST_MSECOND;
7944 g_object_set (storage, "size-time", latency, NULL);
7948 _create_rtpbin (GstWebRTCBin * webrtc)
7952 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
7955 /* mandated by WebRTC */
7956 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
7958 g_object_set (rtpbin, "do-lost", TRUE, NULL);
7960 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
7962 g_signal_connect (rtpbin, "request-pt-map",
7963 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
7964 g_signal_connect (rtpbin, "request-aux-sender",
7965 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
7966 g_signal_connect (rtpbin, "request-aux-receiver",
7967 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
7968 g_signal_connect (rtpbin, "new-storage",
7969 G_CALLBACK (on_rtpbin_new_storage), webrtc);
7970 g_signal_connect (rtpbin, "request-fec-decoder-full",
7971 G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
7972 g_signal_connect (rtpbin, "on-bye-ssrc",
7973 G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
7974 g_signal_connect (rtpbin, "on-bye-timeout",
7975 G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
7976 g_signal_connect (rtpbin, "on-new-ssrc",
7977 G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
7978 g_signal_connect (rtpbin, "on-new-sender-ssrc",
7979 G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
7980 g_signal_connect (rtpbin, "on-sender-ssrc-active",
7981 G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
7982 g_signal_connect (rtpbin, "on-sender-timeout",
7983 G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
7984 g_signal_connect (rtpbin, "on-ssrc-active",
7985 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
7986 g_signal_connect (rtpbin, "on-ssrc-collision",
7987 G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
7988 g_signal_connect (rtpbin, "on-ssrc-sdes",
7989 G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
7990 g_signal_connect (rtpbin, "on-ssrc-validated",
7991 G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
7992 g_signal_connect (rtpbin, "on-timeout",
7993 G_CALLBACK (on_rtpbin_timeout), webrtc);
7994 g_signal_connect (rtpbin, "new-jitterbuffer",
7995 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
8000 static GstStateChangeReturn
8001 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
8003 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
8004 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
8006 GST_DEBUG ("changing state: %s => %s",
8007 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
8008 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
8010 switch (transition) {
8011 case GST_STATE_CHANGE_NULL_TO_READY:{
8012 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
8013 return GST_STATE_CHANGE_FAILURE;
8014 _start_thread (webrtc);
8016 _update_need_negotiation (webrtc);
8020 case GST_STATE_CHANGE_READY_TO_PAUSED:
8021 webrtc->priv->running = TRUE;
8027 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
8028 if (ret == GST_STATE_CHANGE_FAILURE)
8031 switch (transition) {
8032 case GST_STATE_CHANGE_READY_TO_PAUSED:
8033 /* Mangle the return value to NO_PREROLL as that's what really is
8034 * occurring here however cannot be propagated correctly due to nicesrc
8035 * requiring that it be in PLAYING already in order to send/receive
8037 ret = GST_STATE_CHANGE_NO_PREROLL;
8039 case GST_STATE_CHANGE_PAUSED_TO_READY:
8040 webrtc->priv->running = FALSE;
8042 case GST_STATE_CHANGE_READY_TO_NULL:
8043 _stop_thread (webrtc);
8044 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
8045 webrtc->priv->need_negotiation = FALSE;
8055 static GstPadProbeReturn
8056 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
8058 GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
8060 return GST_PAD_PROBE_OK;
8064 peek_sink_buffer (GstWebRTCBin * webrtc, guint rtp_session_id,
8065 guint media_idx, WebRTCTransceiver * trans, GstBuffer * buffer)
8067 GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
8071 if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp))
8073 ssrc = gst_rtp_buffer_get_ssrc (&rtp);
8074 gst_rtp_buffer_unmap (&rtp);
8077 GST_WARNING_OBJECT (webrtc,
8078 "incoming buffer does not contain a valid ssrc");
8084 find_or_add_ssrc_map_item (webrtc,
8085 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session_id, ssrc,
8087 if (item->media_idx == -1) {
8090 GST_DEBUG_OBJECT (webrtc, "updating media idx of ssrc item %p to %u", item,
8092 item->media_idx = media_idx;
8094 /* ensure that the rtx mapping contains a valid ssrc to use for rtx when
8095 * used even when there are no ssrc's in the input/codec preferences caps */
8096 str = g_strdup_printf ("%u", ssrc);
8097 if (!gst_structure_has_field_typed (trans->local_rtx_ssrc_map, str,
8099 /* TODO: ssrc-collision? */
8100 gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
8101 g_random_int (), NULL);
8102 _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
8109 static GstPadProbeReturn
8110 sink_pad_buffer_peek (GstPad * pad, GstPadProbeInfo * info,
8111 GstWebRTCBin * webrtc)
8113 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
8114 WebRTCTransceiver *trans;
8115 guint rtp_session_id, media_idx;
8117 if (!webrtc_pad->trans)
8118 return GST_PAD_PROBE_OK;
8120 trans = (WebRTCTransceiver *) webrtc_pad->trans;
8122 return GST_PAD_PROBE_OK;
8124 rtp_session_id = trans->stream->session_id;
8125 media_idx = webrtc_pad->trans->mline;
8127 if (media_idx != G_MAXUINT)
8128 return GST_PAD_PROBE_OK;
8130 if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
8131 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
8132 peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
8133 } else if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
8134 GstBufferList *list = GST_PAD_PROBE_INFO_BUFFER_LIST (info);
8137 n = gst_buffer_list_length (list);
8138 for (i = 0; i < n; i++) {
8139 GstBuffer *buffer = gst_buffer_list_get (list, i);
8140 peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
8143 g_assert_not_reached ();
8146 return GST_PAD_PROBE_OK;
8150 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
8151 const gchar * name, const GstCaps * caps)
8153 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
8154 GstWebRTCRTPTransceiver *trans = NULL;
8155 GstWebRTCBinPad *pad = NULL;
8157 gboolean lock_mline = FALSE;
8159 if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
8162 if (templ->direction != GST_PAD_SINK ||
8163 g_strcmp0 (templ->name_template, "sink_%u") != 0) {
8164 GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
8170 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
8171 /* no name given when requesting the pad, use next available int */
8172 serial = webrtc->priv->max_sink_pad_serial++;
8174 /* parse serial number from requested padname */
8175 serial = g_ascii_strtoull (&name[5], NULL, 10);
8180 GstWebRTCBinPad *pad2;
8182 trans = _find_transceiver_for_mline (webrtc, serial);
8185 /* Reject transceivers that are only for receiving ... */
8186 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
8187 trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
8188 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8189 " existing m-line %d, but the transceiver's direction is %s",
8191 gst_webrtc_rtp_transceiver_direction_to_string (trans->direction));
8195 /* Reject transceivers that already have a pad allocated */
8196 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
8198 GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
8199 " but the transceiver associated with this m-line already has pad"
8200 " %s", name, serial, GST_PAD_NAME (pad2));
8201 gst_object_unref (pad2);
8206 GST_OBJECT_LOCK (trans);
8207 if (trans->codec_preferences &&
8208 !gst_caps_can_intersect (caps, trans->codec_preferences)) {
8209 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8210 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
8211 " don't match existing codec preferences %" GST_PTR_FORMAT,
8212 name, serial, caps, trans->codec_preferences);
8213 GST_OBJECT_UNLOCK (trans);
8216 GST_OBJECT_UNLOCK (trans);
8218 if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
8219 GstWebRTCKind kind = webrtc_kind_from_caps (caps);
8221 if (trans->kind != kind) {
8222 GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8223 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
8224 " don't match transceiver kind %d",
8225 name, serial, caps, trans->kind);
8233 /* Let's try to find a free transceiver that matches */
8235 GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
8238 kind = webrtc_kind_from_caps (caps);
8240 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
8241 GstWebRTCRTPTransceiver *tmptrans =
8242 g_ptr_array_index (webrtc->priv->transceivers, i);
8243 GstWebRTCBinPad *pad2;
8244 gboolean has_matching_caps;
8246 /* Ignore transceivers with a non-matching kind */
8247 if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
8248 kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
8251 /* Ignore stopped transmitters */
8252 if (tmptrans->stopped)
8255 /* Ignore transceivers that are only for receiving ... */
8256 if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
8257 || tmptrans->direction ==
8258 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
8261 /* Ignore transceivers that already have a pad allocated */
8262 pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
8264 gst_object_unref (pad2);
8268 GST_OBJECT_LOCK (tmptrans);
8269 has_matching_caps = (caps && tmptrans->codec_preferences &&
8270 !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
8271 GST_OBJECT_UNLOCK (tmptrans);
8272 /* Ignore transceivers with non-matching caps */
8273 if (!has_matching_caps)
8282 trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
8283 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
8284 webrtc_kind_from_caps (caps), NULL));
8285 GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
8287 GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
8288 " for mline %u", trans, serial);
8290 if (!_update_transceiver_kind_from_caps (trans, caps)) {
8291 GstWebRTCKind caps_kind = webrtc_kind_from_caps (caps);
8293 GST_WARNING_OBJECT (webrtc,
8294 "Trying to change kind of transceiver %" GST_PTR_FORMAT
8295 " at m-line %d from %s (%d) to %s (%d)", trans, serial,
8296 gst_webrtc_kind_to_string (trans->kind), trans->kind,
8297 gst_webrtc_kind_to_string (caps_kind), caps_kind);
8301 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial, NULL);
8303 pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
8304 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
8305 (GstPadProbeCallback) sink_pad_block, NULL, NULL);
8306 webrtc->priv->pending_sink_transceivers =
8307 g_list_append (webrtc->priv->pending_sink_transceivers,
8308 gst_object_ref (pad));
8310 gst_pad_add_probe (GST_PAD (pad),
8311 GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
8312 (GstPadProbeCallback) sink_pad_buffer_peek, webrtc, NULL);
8315 WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
8316 wtrans->mline_locked = TRUE;
8317 trans->mline = serial;
8322 _add_pad (webrtc, pad);
8324 return GST_PAD (pad);
8332 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
8334 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
8335 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
8337 GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
8339 /* remove the transceiver from the pad so that subsequent code doesn't use
8340 * a possibly dead transceiver */
8342 if (webrtc_pad->trans)
8343 gst_object_unref (webrtc_pad->trans);
8344 webrtc_pad->trans = NULL;
8345 gst_caps_replace (&webrtc_pad->received_caps, NULL);
8348 if (webrtc_pad->block_id) {
8349 gst_pad_remove_probe (GST_PAD (pad), webrtc_pad->block_id);
8350 webrtc_pad->block_id = 0;
8353 _remove_pad (webrtc, webrtc_pad);
8356 _update_need_negotiation (webrtc);
8361 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
8366 /* Add an extra 50 ms for safety */
8367 latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
8368 latency_ns *= GST_MSECOND;
8370 for (i = 0; i < webrtc->priv->transports->len; i++) {
8371 TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
8372 GObject *storage = NULL;
8374 g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
8377 g_object_set (storage, "size-time", latency_ns, NULL);
8379 g_object_unref (storage);
8383 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
8385 _update_drop_probability (GstWebRTCBin * webrtc, gfloat probability, gboolean sender)
8387 GValue value = G_VALUE_INIT;
8388 GstElement *element;
8389 GstIterator *bin_iterator = gst_bin_iterate_sorted (GST_BIN (webrtc));
8390 g_assert (bin_iterator);
8392 while (gst_iterator_next (bin_iterator, &value) == GST_ITERATOR_OK) {
8393 element = GST_ELEMENT (g_value_get_object (&value));
8394 if (g_strrstr (GST_ELEMENT_NAME (element), sender ? "netsim_send" : "netsim_recv"))
8395 g_object_set (element, "drop-probability", probability, NULL);
8396 g_value_reset (&value);
8399 g_value_unset (&value);
8400 gst_iterator_free (bin_iterator);
8405 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
8406 const GValue * value, GParamSpec * pspec)
8408 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8411 case PROP_STUN_SERVER:
8412 gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
8413 g_value_get_string (value));
8415 case PROP_TURN_SERVER:
8416 gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
8417 g_value_get_string (value));
8419 case PROP_BUNDLE_POLICY:
8420 if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
8421 GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
8423 webrtc->bundle_policy = g_value_get_enum (value);
8426 case PROP_ICE_TRANSPORT_POLICY:
8427 webrtc->ice_transport_policy = g_value_get_enum (value);
8428 gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
8429 webrtc->ice_transport_policy ==
8430 GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
8433 g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
8434 webrtc->priv->jb_latency = g_value_get_uint (value);
8435 _update_rtpstorage_latency (webrtc);
8437 case PROP_ICE_AGENT:
8438 webrtc->priv->ice = g_value_get_object (value);
8440 case PROP_HTTP_PROXY:
8441 gst_webrtc_ice_set_http_proxy (webrtc->priv->ice,
8442 g_value_get_string (value));
8444 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
8446 webrtc->priv->netsim = g_value_get_boolean (value);
8447 _update_drop_probability (webrtc, webrtc->priv->netsim ?
8448 webrtc->priv->drop_probability_sender : 0.0, TRUE);
8449 _update_drop_probability (webrtc, webrtc->priv->netsim ?
8450 webrtc->priv->drop_probability_receiver : 0.0, FALSE);
8452 case PROP_DROP_PROBABILITY_SENDER:
8453 webrtc->priv->drop_probability_sender = g_value_get_float (value);
8454 _update_drop_probability (webrtc, webrtc->priv->drop_probability_sender, TRUE);
8456 case PROP_DROP_PROBABILITY_RECEIVER:
8457 webrtc->priv->drop_probability_receiver = g_value_get_float (value);
8458 _update_drop_probability (webrtc, webrtc->priv->drop_probability_receiver, FALSE);
8462 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8468 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
8469 GValue * value, GParamSpec * pspec)
8471 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8475 case PROP_CONNECTION_STATE:
8476 g_value_set_enum (value, webrtc->peer_connection_state);
8478 case PROP_SIGNALING_STATE:
8479 g_value_set_enum (value, webrtc->signaling_state);
8481 case PROP_ICE_GATHERING_STATE:
8482 g_value_set_enum (value, webrtc->ice_gathering_state);
8484 case PROP_ICE_CONNECTION_STATE:
8485 g_value_set_enum (value, webrtc->ice_connection_state);
8487 case PROP_LOCAL_DESCRIPTION:
8488 if (webrtc->pending_local_description)
8489 g_value_set_boxed (value, webrtc->pending_local_description);
8490 else if (webrtc->current_local_description)
8491 g_value_set_boxed (value, webrtc->current_local_description);
8493 g_value_set_boxed (value, NULL);
8495 case PROP_CURRENT_LOCAL_DESCRIPTION:
8496 g_value_set_boxed (value, webrtc->current_local_description);
8498 case PROP_PENDING_LOCAL_DESCRIPTION:
8499 g_value_set_boxed (value, webrtc->pending_local_description);
8501 case PROP_REMOTE_DESCRIPTION:
8502 if (webrtc->pending_remote_description)
8503 g_value_set_boxed (value, webrtc->pending_remote_description);
8504 else if (webrtc->current_remote_description)
8505 g_value_set_boxed (value, webrtc->current_remote_description);
8507 g_value_set_boxed (value, NULL);
8509 case PROP_CURRENT_REMOTE_DESCRIPTION:
8510 g_value_set_boxed (value, webrtc->current_remote_description);
8512 case PROP_PENDING_REMOTE_DESCRIPTION:
8513 g_value_set_boxed (value, webrtc->pending_remote_description);
8515 case PROP_STUN_SERVER:
8516 g_value_take_string (value,
8517 gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
8519 case PROP_TURN_SERVER:
8520 g_value_take_string (value,
8521 gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
8523 case PROP_BUNDLE_POLICY:
8524 g_value_set_enum (value, webrtc->bundle_policy);
8526 case PROP_ICE_TRANSPORT_POLICY:
8527 g_value_set_enum (value, webrtc->ice_transport_policy);
8529 case PROP_ICE_AGENT:
8530 g_value_set_object (value, webrtc->priv->ice);
8533 g_value_set_uint (value, webrtc->priv->jb_latency);
8535 case PROP_SCTP_TRANSPORT:
8536 g_value_set_object (value, webrtc->priv->sctp_transport);
8538 case PROP_HTTP_PROXY:
8539 g_value_take_string (value,
8540 gst_webrtc_ice_get_http_proxy (webrtc->priv->ice));
8542 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
8544 g_value_set_boolean (value, webrtc->priv->netsim);
8546 case PROP_DROP_PROBABILITY_SENDER:
8547 g_value_set_float (value, webrtc->priv->drop_probability_sender);
8549 case PROP_DROP_PROBABILITY_RECEIVER:
8550 g_value_set_float (value, webrtc->priv->drop_probability_receiver);
8554 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8561 gst_webrtc_bin_constructed (GObject * object)
8563 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8566 if (!webrtc->priv->ice) {
8567 name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
8568 webrtc->priv->ice = GST_WEBRTC_ICE (gst_webrtc_nice_new (name));
8571 gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
8572 (GstWebRTCICEOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
8574 G_OBJECT_CLASS (parent_class)->constructed (object);
8578 _free_pending_pad (GstPad * pad)
8580 gst_object_unref (pad);
8584 gst_webrtc_bin_dispose (GObject * object)
8586 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8588 if (webrtc->priv->ice)
8589 gst_object_unref (webrtc->priv->ice);
8590 webrtc->priv->ice = NULL;
8592 if (webrtc->priv->ice_stream_map)
8593 g_array_free (webrtc->priv->ice_stream_map, TRUE);
8594 webrtc->priv->ice_stream_map = NULL;
8596 g_clear_object (&webrtc->priv->sctp_transport);
8598 G_OBJECT_CLASS (parent_class)->dispose (object);
8602 gst_webrtc_bin_finalize (GObject * object)
8604 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8606 if (webrtc->priv->transports)
8607 g_ptr_array_free (webrtc->priv->transports, TRUE);
8608 webrtc->priv->transports = NULL;
8610 if (webrtc->priv->transceivers)
8611 g_ptr_array_free (webrtc->priv->transceivers, TRUE);
8612 webrtc->priv->transceivers = NULL;
8614 if (webrtc->priv->data_channels)
8615 g_ptr_array_free (webrtc->priv->data_channels, TRUE);
8616 webrtc->priv->data_channels = NULL;
8618 if (webrtc->priv->pending_data_channels)
8619 g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
8620 webrtc->priv->pending_data_channels = NULL;
8622 if (webrtc->priv->pending_remote_ice_candidates)
8623 g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
8624 webrtc->priv->pending_remote_ice_candidates = NULL;
8626 if (webrtc->priv->pending_local_ice_candidates)
8627 g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
8628 webrtc->priv->pending_local_ice_candidates = NULL;
8630 if (webrtc->priv->pending_pads)
8631 g_list_free_full (webrtc->priv->pending_pads,
8632 (GDestroyNotify) _free_pending_pad);
8633 webrtc->priv->pending_pads = NULL;
8635 if (webrtc->priv->pending_sink_transceivers)
8636 g_list_free_full (webrtc->priv->pending_sink_transceivers,
8637 (GDestroyNotify) gst_object_unref);
8638 webrtc->priv->pending_sink_transceivers = NULL;
8640 if (webrtc->current_local_description)
8641 gst_webrtc_session_description_free (webrtc->current_local_description);
8642 webrtc->current_local_description = NULL;
8643 if (webrtc->pending_local_description)
8644 gst_webrtc_session_description_free (webrtc->pending_local_description);
8645 webrtc->pending_local_description = NULL;
8647 if (webrtc->current_remote_description)
8648 gst_webrtc_session_description_free (webrtc->current_remote_description);
8649 webrtc->current_remote_description = NULL;
8650 if (webrtc->pending_remote_description)
8651 gst_webrtc_session_description_free (webrtc->pending_remote_description);
8652 webrtc->pending_remote_description = NULL;
8654 if (webrtc->priv->last_generated_answer)
8655 gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
8656 webrtc->priv->last_generated_answer = NULL;
8657 if (webrtc->priv->last_generated_offer)
8658 gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
8659 webrtc->priv->last_generated_offer = NULL;
8661 g_mutex_clear (DC_GET_LOCK (webrtc));
8662 g_mutex_clear (ICE_GET_LOCK (webrtc));
8663 g_mutex_clear (PC_GET_LOCK (webrtc));
8664 g_cond_clear (PC_GET_COND (webrtc));
8666 G_OBJECT_CLASS (parent_class)->finalize (object);
8670 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
8672 GObjectClass *gobject_class = (GObjectClass *) klass;
8673 GstElementClass *element_class = (GstElementClass *) klass;
8675 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
8676 element_class->release_pad = gst_webrtc_bin_release_pad;
8677 element_class->change_state = gst_webrtc_bin_change_state;
8679 gst_element_class_add_static_pad_template_with_gtype (element_class,
8680 &sink_template, GST_TYPE_WEBRTC_BIN_SINK_PAD);
8681 gst_element_class_add_static_pad_template_with_gtype (element_class,
8682 &src_template, GST_TYPE_WEBRTC_BIN_SRC_PAD);
8684 gst_element_class_set_metadata (element_class, "WebRTC Bin",
8685 "Filter/Network/WebRTC", "A bin for webrtc connections",
8686 "Matthew Waters <matthew@centricular.com>");
8688 gobject_class->constructed = gst_webrtc_bin_constructed;
8689 gobject_class->get_property = gst_webrtc_bin_get_property;
8690 gobject_class->set_property = gst_webrtc_bin_set_property;
8691 gobject_class->dispose = gst_webrtc_bin_dispose;
8692 gobject_class->finalize = gst_webrtc_bin_finalize;
8694 g_object_class_install_property (gobject_class,
8695 PROP_LOCAL_DESCRIPTION,
8696 g_param_spec_boxed ("local-description", "Local Description",
8697 "The local SDP description in use for this connection. "
8698 "Favours a pending description over the current description",
8699 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8700 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8702 g_object_class_install_property (gobject_class,
8703 PROP_CURRENT_LOCAL_DESCRIPTION,
8704 g_param_spec_boxed ("current-local-description",
8705 "Current Local Description",
8706 "The local description that was successfully negotiated the last time "
8707 "the connection transitioned into the stable state",
8708 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8709 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8711 g_object_class_install_property (gobject_class,
8712 PROP_PENDING_LOCAL_DESCRIPTION,
8713 g_param_spec_boxed ("pending-local-description",
8714 "Pending Local Description",
8715 "The local description that is in the process of being negotiated plus "
8716 "any local candidates that have been generated by the ICE Agent since the "
8717 "offer or answer was created",
8718 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8719 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8721 g_object_class_install_property (gobject_class,
8722 PROP_REMOTE_DESCRIPTION,
8723 g_param_spec_boxed ("remote-description", "Remote Description",
8724 "The remote SDP description to use for this connection. "
8725 "Favours a pending description over the current description",
8726 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8727 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8729 g_object_class_install_property (gobject_class,
8730 PROP_CURRENT_REMOTE_DESCRIPTION,
8731 g_param_spec_boxed ("current-remote-description",
8732 "Current Remote Description",
8733 "The last remote description that was successfully negotiated the last "
8734 "time the connection transitioned into the stable state plus any remote "
8735 "candidates that have been supplied via addIceCandidate() since the offer "
8736 "or answer was created",
8737 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8738 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8740 g_object_class_install_property (gobject_class,
8741 PROP_PENDING_REMOTE_DESCRIPTION,
8742 g_param_spec_boxed ("pending-remote-description",
8743 "Pending Remote Description",
8744 "The remote description that is in the process of being negotiated, "
8745 "complete with any remote candidates that have been supplied via "
8746 "addIceCandidate() since the offer or answer was created",
8747 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8748 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8750 g_object_class_install_property (gobject_class,
8752 g_param_spec_string ("stun-server", "STUN Server",
8753 "The STUN server of the form stun://hostname:port",
8754 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8756 g_object_class_install_property (gobject_class,
8758 g_param_spec_string ("turn-server", "TURN Server",
8759 "The TURN server of the form turn(s)://username:password@host:port. "
8760 "To use time-limited credentials, the form must be turn(s)://timestamp:"
8761 "username:password@host:port. Please note that the ':' character of "
8762 "the 'timestamp:username' and the 'password' encoded by base64 should "
8763 "be escaped to be parsed properly. "
8764 "This is a convenience property, use #GstWebRTCBin::add-turn-server "
8765 "if you wish to use multiple TURN servers",
8766 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8768 g_object_class_install_property (gobject_class,
8769 PROP_CONNECTION_STATE,
8770 g_param_spec_enum ("connection-state", "Connection State",
8771 "The overall connection state of this element",
8772 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
8773 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
8774 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8776 g_object_class_install_property (gobject_class,
8777 PROP_SIGNALING_STATE,
8778 g_param_spec_enum ("signaling-state", "Signaling State",
8779 "The signaling state of this element",
8780 GST_TYPE_WEBRTC_SIGNALING_STATE,
8781 GST_WEBRTC_SIGNALING_STATE_STABLE,
8782 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8784 g_object_class_install_property (gobject_class,
8785 PROP_ICE_CONNECTION_STATE,
8786 g_param_spec_enum ("ice-connection-state", "ICE connection state",
8787 "The collective connection state of all ICETransport's",
8788 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
8789 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
8790 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8792 g_object_class_install_property (gobject_class,
8793 PROP_ICE_GATHERING_STATE,
8794 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
8795 "The collective gathering state of all ICETransport's",
8796 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
8797 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
8798 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8800 g_object_class_install_property (gobject_class,
8802 g_param_spec_enum ("bundle-policy", "Bundle Policy",
8803 "The policy to apply for bundling",
8804 GST_TYPE_WEBRTC_BUNDLE_POLICY,
8805 GST_WEBRTC_BUNDLE_POLICY_NONE,
8806 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8808 g_object_class_install_property (gobject_class,
8809 PROP_ICE_TRANSPORT_POLICY,
8810 g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
8811 "The policy to apply for ICE transport",
8812 GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
8813 GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
8814 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8816 g_object_class_install_property (gobject_class,
8818 g_param_spec_object ("ice-agent", "WebRTC ICE agent",
8819 "The WebRTC ICE agent",
8820 GST_TYPE_WEBRTC_ICE,
8821 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
8824 * GstWebRTCBin:latency:
8826 * Default duration to buffer in the jitterbuffers (in ms)
8831 g_object_class_install_property (gobject_class,
8833 g_param_spec_uint ("latency", "Latency",
8834 "Default duration to buffer in the jitterbuffers (in ms)",
8835 0, G_MAXUINT, DEFAULT_JB_LATENCY,
8836 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8839 * GstWebRTCBin:http-proxy:
8841 * A HTTP proxy for use with TURN/TCP of the form
8842 * http://[username:password@]hostname[:port]
8846 g_object_class_install_property (gobject_class,
8848 g_param_spec_string ("http-proxy", "HTTP Proxy",
8849 "A HTTP proxy for use with TURN/TCP of the form "
8850 "http://[username:password@]hostname[:port]",
8851 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8854 * GstWebRTCBin:sctp-transport:
8856 * The WebRTC SCTP Transport
8860 g_object_class_install_property (gobject_class,
8861 PROP_SCTP_TRANSPORT,
8862 g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
8863 "The WebRTC SCTP Transport",
8864 GST_TYPE_WEBRTC_SCTP_TRANSPORT,
8865 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8867 #ifdef TIZEN_FEATURE_WEBRTC_IMPORT_NETSIM
8868 g_object_class_install_property (gobject_class,
8870 g_param_spec_boolean ("netsim", "Use network simulator",
8871 "Use network simulator for packet loss",
8872 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8874 g_object_class_install_property (gobject_class,
8875 PROP_DROP_PROBABILITY_SENDER,
8876 g_param_spec_float ("drop-probability-sender", "Drop Probability for sender",
8877 "The Probability a sending RTP buffer is dropped",
8879 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
8881 g_object_class_install_property (gobject_class,
8882 PROP_DROP_PROBABILITY_RECEIVER,
8883 g_param_spec_float ("drop-probability-receiver", "Drop Probability for receiver",
8884 "The Probability a received RTP buffer is dropped",
8886 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
8891 * GstWebRTCBin::create-offer:
8892 * @object: the #webrtcbin
8893 * @options: (nullable): create-offer options
8894 * @promise: a #GstPromise which will contain the offer
8896 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
8897 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
8898 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8899 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
8900 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8903 * GstWebRTCBin::create-answer:
8904 * @object: the #webrtcbin
8905 * @options: (nullable): create-answer options
8906 * @promise: a #GstPromise which will contain the answer
8908 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
8909 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
8910 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8911 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
8912 G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8915 * GstWebRTCBin::set-local-description:
8916 * @object: the #GstWebRTCBin
8917 * @desc: a #GstWebRTCSessionDescription description
8918 * @promise: (nullable): a #GstPromise to be notified when it's set
8920 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
8921 g_signal_new_class_handler ("set-local-description",
8922 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8923 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
8924 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8927 * GstWebRTCBin::set-remote-description:
8928 * @object: the #GstWebRTCBin
8929 * @desc: a #GstWebRTCSessionDescription description
8930 * @promise: (nullable): a #GstPromise to be notified when it's set
8932 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
8933 g_signal_new_class_handler ("set-remote-description",
8934 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8935 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
8936 G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8939 * GstWebRTCBin::add-ice-candidate:
8940 * @object: the #webrtcbin
8941 * @mline_index: the index of the media description in the SDP
8942 * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
8945 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
8946 g_signal_new_class_handler ("add-ice-candidate",
8947 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8948 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
8949 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8952 * GstWebRTCBin::get-stats:
8953 * @object: the #webrtcbin
8954 * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
8955 * @promise: a #GstPromise for the result
8957 * The @promise will contain the result of retrieving the session statistics.
8958 * The structure will be named 'application/x-webrtc-stats and contain the
8959 * following based on the webrtc-stats spec available from
8960 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
8961 * and is constantly changing these statistics may be changed to fit with
8964 * Each field key is a unique identifier for each RTCStats
8965 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
8966 * GstStructure) in the RTCStatsReport
8967 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
8968 * field in the RTCStats subclass is outlined below.
8970 * Each statistics structure contains the following values as defined by
8971 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
8973 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
8974 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
8975 * "id" G_TYPE_STRING unique identifier
8977 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
8979 * "payload-type" G_TYPE_UINT the rtp payload number in use
8980 * "clock-rate" G_TYPE_UINT the rtp clock-rate
8982 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
8984 * "ssrc" G_TYPE_STRING the rtp sequence src in use
8985 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
8986 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
8987 * "kind" G_TYPE_STRING either "audio" or "video", depending on the associated transceiver (Since: 1.22)
8989 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
8991 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
8992 * "packets-lost" G_TYPE_INT64 number of packets lost
8993 * "packets-discarded" G_TYPE_UINT64 number of packets discarded
8994 * "packets-repaired" G_TYPE_UINT64 number of packets repaired
8995 * "jitter" G_TYPE_DOUBLE packet jitter measured in seconds
8997 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
8999 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
9000 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
9001 * "packets-duplicated" G_TYPE_UINT64 number of packets duplicated
9002 * "fir-count" G_TYPE_UINT FIR packets sent by the receiver
9003 * "pli-count" G_TYPE_UINT PLI packets sent by the receiver
9004 * "nack-count" G_TYPE_UINT NACK packets sent by the receiver
9006 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
9008 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
9009 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
9010 * "fraction-lost" G_TYPE_DOUBLE fraction packet loss
9012 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
9014 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
9015 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
9017 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
9019 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats (optional since 1.22)
9020 * "fir-count" G_TYPE_UINT FIR packets received by the sender
9021 * "pli-count" G_TYPE_UINT PLI packets received by the sender
9022 * "nack-count" G_TYPE_UINT NACK packets received by the sender
9024 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
9026 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
9027 * "remote-timestamp" G_TYPE_DOUBLE remote timestamp the statistics were sent by the remote
9029 * RTCIceCandidateStats supported fields (https://www.w3.org/TR/webrtc-stats/#icecandidate-dict*) (Since: 1.22)
9031 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
9032 * "address" G_TYPE_STRING address of the candidate, allowing for IPv4, IPv6 and FQDNs
9033 * "port" G_TYPE_UINT port number of the candidate
9034 * "candidate-type" G_TYPE_STRING RTCIceCandidateType
9035 * "priority" G_TYPE_UINT64 calculated as defined in RFC 5245
9036 * "protocol" G_TYPE_STRING Either "udp" or "tcp". Based on the "transport" defined in RFC 5245
9037 * "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"
9038 * "url" G_TYPE_STRING URL of the ICE server from which the candidate was obtained. Only present for local candidates
9040 * RTCIceCandidatePairStats supported fields (https://www.w3.org/TR/webrtc-stats/#candidatepair-dict*) (Since: 1.22)
9042 * "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.
9043 * "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.
9045 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
9047 * RTCPeerConnectionStats supported fields (https://w3c.github.io/webrtc-stats/#pcstats-dict*)
9049 * "data-channels-opened" G_TYPE_UINT number of unique data channels that have entered the 'open' state
9050 * "data-channels-closed" G_TYPE_UINT number of unique data channels that have left the 'open' state
9054 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
9055 g_signal_new_class_handler ("get-stats",
9056 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
9057 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
9058 G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
9061 * GstWebRTCBin::on-negotiation-needed:
9062 * @object: the #webrtcbin
9064 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
9065 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
9066 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
9069 * GstWebRTCBin::on-ice-candidate:
9070 * @object: the #webrtcbin
9071 * @mline_index: the index of the media description in the SDP
9072 * @candidate: the ICE candidate
9074 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
9075 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
9076 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
9077 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
9080 * GstWebRTCBin::on-new-transceiver:
9081 * @object: the #webrtcbin
9082 * @candidate: the new #GstWebRTCRTPTransceiver
9084 gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
9085 g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
9086 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
9087 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
9090 * GstWebRTCBin::on-data-channel:
9091 * @object: the #GstWebRTCBin
9092 * @channel: the new `GstWebRTCDataChannel`
9094 gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
9095 g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
9096 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
9097 G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
9100 * GstWebRTCBin::prepare-data-channel:
9101 * @object: the #GstWebRTCBin
9102 * @channel: the new `GstWebRTCDataChannel`
9103 * @is_local: Whether this channel is local or remote
9105 * Allows data-channel consumers to configure signal handlers on a newly
9106 * created data-channel, before any data or state change has been notified.
9110 gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL] =
9111 g_signal_new ("prepare-data-channel", G_TYPE_FROM_CLASS (klass),
9112 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2,
9113 GST_TYPE_WEBRTC_DATA_CHANNEL, G_TYPE_BOOLEAN);
9116 * GstWebRTCBin::request-aux-sender:
9117 * @object: the #GstWebRTCBin
9118 * @dtls-transport: The #GstWebRTCDTLSTransport object for which the aux
9119 * sender will be used.
9121 * Request an AUX sender element for the given @dtls-transport.
9123 * Returns: (transfer full): A new GStreamer element
9127 gst_webrtc_bin_signals[REQUEST_AUX_SENDER] =
9128 g_signal_new ("request-aux-sender", G_TYPE_FROM_CLASS (klass),
9129 G_SIGNAL_RUN_LAST, 0, _gst_element_accumulator, NULL, NULL,
9130 GST_TYPE_ELEMENT, 1, GST_TYPE_WEBRTC_DTLS_TRANSPORT);
9133 * GstWebRTCBin::add-transceiver:
9134 * @object: the #webrtcbin
9135 * @direction: the direction of the new transceiver
9136 * @caps: (allow none): the codec preferences for this transceiver
9138 * Returns: the new #GstWebRTCRTPTransceiver
9140 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
9141 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
9142 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
9143 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
9144 NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
9145 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
9148 * GstWebRTCBin::get-transceivers:
9149 * @object: the #webrtcbin
9151 * Returns: a #GArray of #GstWebRTCRTPTransceivers
9153 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
9154 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
9155 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
9156 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
9160 * GstWebRTCBin::get-transceiver:
9161 * @object: the #GstWebRTCBin
9162 * @idx: The index of the transceiver
9164 * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
9167 gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
9168 g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
9169 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
9170 G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
9171 GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
9174 * GstWebRTCBin::add-turn-server:
9175 * @object: the #GstWebRTCBin
9176 * @uri: The uri of the server of the form turn(s)://username:password@host:port
9178 * Add a turn server to obtain ICE candidates from
9180 gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
9181 g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
9182 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
9183 G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
9184 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
9187 * GstWebRTCBin::create-data-channel:
9188 * @object: the #GstWebRTCBin
9189 * @label: the label for the data channel
9190 * @options: a #GstStructure of options for creating the data channel
9192 * The options dictionary is the same format as the RTCDataChannelInit
9193 * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
9194 * and reproduced below
9196 * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering
9197 * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
9198 * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping
9199 * protocol G_TYPE_STRING The subprotocol used by this channel
9200 * 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.
9201 * id G_TYPE_INT Override the default identifier selection of this channel
9202 * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel
9204 * Returns: (transfer full): a new data channel object
9206 gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
9207 g_signal_new_class_handler ("create-data-channel",
9208 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
9209 G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
9210 NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
9212 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
9213 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SINK_PAD, 0);
9214 gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SRC_PAD, 0);
9218 _unparent_and_unref (GObject * object)
9220 GstObject *obj = GST_OBJECT (object);
9222 GST_OBJECT_PARENT (obj) = NULL;
9224 gst_object_unref (obj);
9228 _transport_free (GObject * object)
9230 TransportStream *stream = (TransportStream *) object;
9231 GstWebRTCBin *webrtc;
9233 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
9235 if (stream->transport) {
9236 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
9237 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
9240 gst_object_unref (object);
9244 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
9246 /* Set SINK/SRC flags as webrtcbin can act as one depending on the
9247 * SDP later. Without setting this here already, surrounding bins might not
9248 * notice this and the pipeline configuration might become inconsistent,
9249 * e.g. with regards to latency.
9250 * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
9252 gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
9253 GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
9254 GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
9256 webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
9257 g_mutex_init (PC_GET_LOCK (webrtc));
9258 g_cond_init (PC_GET_COND (webrtc));
9260 g_mutex_init (ICE_GET_LOCK (webrtc));
9261 g_mutex_init (DC_GET_LOCK (webrtc));
9263 webrtc->rtpbin = _create_rtpbin (webrtc);
9264 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
9266 webrtc->priv->transceivers =
9267 g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
9268 webrtc->priv->transports =
9269 g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
9271 webrtc->priv->data_channels =
9272 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
9274 webrtc->priv->pending_data_channels =
9275 g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
9277 webrtc->priv->ice_stream_map =
9278 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
9279 webrtc->priv->pending_remote_ice_candidates =
9280 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
9281 g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
9282 (GDestroyNotify) _clear_ice_candidate_item);
9284 webrtc->priv->pending_local_ice_candidates =
9285 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
9286 g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
9287 (GDestroyNotify) _clear_ice_candidate_item);
9289 /* we start off closed until we move to READY */
9290 webrtc->priv->is_closed = TRUE;
9291 webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;