webrtcbin: bind transceiver's fec-percentage to encoder percentage
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / webrtc / gstwebrtcbin.c
1 /* GStreamer
2  * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
28 #include "utils.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31 #include "webrtcdatachannel.h"
32 #include "webrtcsctptransport.h"
33
34 #include "gst/webrtc/webrtc-priv.h"
35
36 #include <gst/rtp/rtp.h>
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #define RANDOM_SESSION_ID \
43     ((((((guint64) g_random_int()) << 32) | \
44        (guint64) g_random_int ())) & \
45     G_GUINT64_CONSTANT (0x7fffffffffffffff))
46
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)))
50
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)))
55
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)))
59
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)))
63
64 /* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */
65 #define RTPSTORAGE_EXTRA_TIME (50)
66
67 #define DEFAULT_JB_LATENCY 200
68
69 /**
70  * SECTION: element-webrtcbin
71  * title: webrtcbin
72  *
73  * This webrtcbin implements the majority of the W3's peerconnection API and
74  * implementation guide where possible. Generating offers, answers and setting
75  * local and remote SDP's are all supported.  Both media descriptions and
76  * descriptions involving data channels are supported.
77  *
78  * Each input/output pad is equivalent to a Track in W3 parlance which are
79  * added/removed from the bin.  The number of requested sink pads is the number
80  * of streams that will be sent to the receiver and will be associated with a
81  * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
82  *
83  * On the receiving side, RTPTransceiver's are created in response to setting
84  * a remote description.  Output pads for the receiving streams in the set
85  * description are also created when data is received.
86  *
87  * A TransportStream is created when needed in order to transport the data over
88  * the necessary DTLS/ICE channel to the peer.  The exact configuration depends
89  * on the negotiated SDP's between the peers based on the bundle and rtcp
90  * configuration.  Some cases are outlined below for a simple single
91  * audio/video/data session:
92  *
93  * - max-bundle uses a single transport for all
94  *   media/data transported.  Renegotiation involves adding/removing the
95  *   necessary streams to the existing transports.
96  * - max-compat involves two TransportStream per media stream
97  *   to transport the rtp and the rtcp packets and a single TransportStream for
98  *   all data channels.  Each stream change involves modifying the associated
99  *   TransportStream/s as necessary.
100  */
101
102 /*
103  * TODO:
104  * assert sending payload type matches the stream
105  * reconfiguration (of anything)
106  * LS groups
107  * balanced bundle policy
108  * setting custom DTLS certificates
109  *
110  * separate session id's from mlineindex properly
111  * how to deal with replacing a input/output track/stream
112  */
113
114 static void _update_need_negotiation (GstWebRTCBin * webrtc);
115 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
116     GstWebRTCBinPad * pad);
117
118
119 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
120 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
121
122 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
123     GST_PAD_SINK,
124     GST_PAD_REQUEST,
125     GST_STATIC_CAPS ("application/x-rtp"));
126
127 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
128     GST_PAD_SRC,
129     GST_PAD_SOMETIMES,
130     GST_STATIC_CAPS ("application/x-rtp"));
131
132 enum
133 {
134   PROP_PAD_TRANSCEIVER = 1,
135 };
136
137 static gboolean
138 _have_nice_elements (GstWebRTCBin * webrtc)
139 {
140   GstPluginFeature *feature;
141
142   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
143   if (feature) {
144     gst_object_unref (feature);
145   } else {
146     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
147         ("%s", "libnice elements are not available"));
148     return FALSE;
149   }
150
151   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
152   if (feature) {
153     gst_object_unref (feature);
154   } else {
155     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
156         ("%s", "libnice elements are not available"));
157     return FALSE;
158   }
159
160   return TRUE;
161 }
162
163 static gboolean
164 _have_sctp_elements (GstWebRTCBin * webrtc)
165 {
166   GstPluginFeature *feature;
167
168   feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
169   if (feature) {
170     gst_object_unref (feature);
171   } else {
172     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
173         ("%s", "sctp elements are not available"));
174     return FALSE;
175   }
176
177   feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
178   if (feature) {
179     gst_object_unref (feature);
180   } else {
181     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
182         ("%s", "sctp elements are not available"));
183     return FALSE;
184   }
185
186   return TRUE;
187 }
188
189 static gboolean
190 _have_dtls_elements (GstWebRTCBin * webrtc)
191 {
192   GstPluginFeature *feature;
193
194   feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
195   if (feature) {
196     gst_object_unref (feature);
197   } else {
198     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
199         ("%s", "dtls elements are not available"));
200     return FALSE;
201   }
202
203   feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
204   if (feature) {
205     gst_object_unref (feature);
206   } else {
207     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
208         ("%s", "dtls elements are not available"));
209     return FALSE;
210   }
211
212   return TRUE;
213 }
214
215 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
216
217 static void
218 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
219     GValue * value, GParamSpec * pspec)
220 {
221   GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
222
223   switch (prop_id) {
224     case PROP_PAD_TRANSCEIVER:
225       g_value_set_object (value, pad->trans);
226       break;
227     default:
228       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
229       break;
230   }
231 }
232
233 static void
234 gst_webrtc_bin_pad_finalize (GObject * object)
235 {
236   GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
237
238   if (pad->trans)
239     gst_object_unref (pad->trans);
240   pad->trans = NULL;
241
242   if (pad->received_caps)
243     gst_caps_unref (pad->received_caps);
244   pad->received_caps = NULL;
245
246   G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
247 }
248
249 static void
250 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
251 {
252   GObjectClass *gobject_class = (GObjectClass *) klass;
253
254   gobject_class->get_property = gst_webrtc_bin_pad_get_property;
255   gobject_class->finalize = gst_webrtc_bin_pad_finalize;
256
257   g_object_class_install_property (gobject_class,
258       PROP_PAD_TRANSCEIVER,
259       g_param_spec_object ("transceiver", "Transceiver",
260           "Transceiver associated with this pad",
261           GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
262           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
263 }
264
265 static void
266 gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
267 {
268   if (wpad->received_caps) {
269     WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
270     GstPad *pad = GST_PAD (wpad);
271
272     gst_event_take (&trans->ssrc_event,
273         gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
274             gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
275                 trans->current_ssrc, NULL)));
276
277     gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
278   }
279 }
280
281 static GList *
282 _get_pending_sink_transceiver (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
283 {
284   GList *ret;
285
286   for (ret = webrtc->priv->pending_sink_transceivers; ret; ret = ret->next) {
287     if (ret->data == pad)
288       break;
289   }
290
291   return ret;
292 }
293
294 static gboolean
295 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
296 {
297   GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
298   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
299   gboolean check_negotiation = FALSE;
300
301   if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
302     GstCaps *caps;
303
304     gst_event_parse_caps (event, &caps);
305     check_negotiation = (!wpad->received_caps
306         || !gst_caps_is_equal (wpad->received_caps, caps));
307     gst_caps_replace (&wpad->received_caps, caps);
308
309     GST_DEBUG_OBJECT (parent,
310         "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
311         GST_PTR_FORMAT, pad, check_negotiation, caps);
312
313     if (check_negotiation) {
314       WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
315       const GstStructure *s;
316
317       s = gst_caps_get_structure (caps, 0);
318       gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
319       gst_webrtc_bin_pad_update_ssrc_event (wpad);
320     }
321
322     /* A remote description might have been set while the pad hadn't
323      * yet received caps, delaying the connection of the input stream
324      */
325     PC_LOCK (webrtc);
326     if (wpad->trans) {
327       GST_OBJECT_LOCK (wpad->trans);
328       if (wpad->trans->current_direction ==
329           GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY
330           || wpad->trans->current_direction ==
331           GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
332         GList *pending = _get_pending_sink_transceiver (webrtc, wpad);
333
334         if (pending) {
335           GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
336               "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
337               wpad->trans, wpad->received_caps);
338           _connect_input_stream (webrtc, wpad);
339           gst_pad_remove_probe (GST_PAD (pad), wpad->block_id);
340           wpad->block_id = 0;
341           gst_object_unref (pending->data);
342           webrtc->priv->pending_sink_transceivers =
343               g_list_delete_link (webrtc->priv->pending_sink_transceivers,
344               pending);
345         }
346       }
347       GST_OBJECT_UNLOCK (wpad->trans);
348     }
349     PC_UNLOCK (webrtc);
350   } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
351     check_negotiation = TRUE;
352   }
353
354   if (check_negotiation) {
355     PC_LOCK (webrtc);
356     _update_need_negotiation (webrtc);
357     PC_UNLOCK (webrtc);
358   }
359
360   return gst_pad_event_default (pad, parent, event);
361 }
362
363 static gboolean
364 gst_webrtcbin_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
365 {
366   GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
367   gboolean ret = FALSE;
368
369   switch (GST_QUERY_TYPE (query)) {
370     case GST_QUERY_ACCEPT_CAPS:
371       GST_OBJECT_LOCK (wpad->trans);
372       if (wpad->trans->codec_preferences) {
373         GstCaps *caps;
374
375         gst_query_parse_accept_caps (query, &caps);
376
377         gst_query_set_accept_caps_result (query,
378             gst_caps_can_intersect (caps, wpad->trans->codec_preferences));
379         ret = TRUE;
380       }
381       GST_OBJECT_UNLOCK (wpad->trans);
382       break;
383
384     case GST_QUERY_CAPS:
385     {
386       GstCaps *codec_preferences = NULL;
387
388       GST_OBJECT_LOCK (wpad->trans);
389       if (wpad->trans->codec_preferences)
390         codec_preferences = gst_caps_ref (wpad->trans->codec_preferences);
391       GST_OBJECT_UNLOCK (wpad->trans);
392
393       if (codec_preferences) {
394         GstCaps *filter = NULL;
395         GstCaps *filter_prefs = NULL;
396         GstPad *target;
397
398         gst_query_parse_caps (query, &filter);
399
400         if (filter) {
401           filter_prefs = gst_caps_intersect_full (filter, codec_preferences,
402               GST_CAPS_INTERSECT_FIRST);
403           gst_caps_unref (codec_preferences);
404         } else {
405           filter_prefs = codec_preferences;
406         }
407
408         target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
409         if (target) {
410           GstCaps *result;
411
412           result = gst_pad_query_caps (target, filter_prefs);
413           gst_query_set_caps_result (query, result);
414           gst_caps_unref (result);
415
416           gst_object_unref (target);
417         } else {
418           gst_query_set_caps_result (query, filter_prefs);
419         }
420
421         gst_caps_unref (filter_prefs);
422         ret = TRUE;
423       }
424       break;
425     }
426     default:
427       break;
428   }
429
430   if (ret)
431     return TRUE;
432
433   return gst_pad_query_default (pad, parent, query);
434 }
435
436
437 static void
438 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
439 {
440 }
441
442 static GstWebRTCBinPad *
443 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
444 {
445   GstWebRTCBinPad *pad;
446   GstPadTemplate *template;
447
448   if (direction == GST_PAD_SINK)
449     template = gst_static_pad_template_get (&sink_template);
450   else if (direction == GST_PAD_SRC)
451     template = gst_static_pad_template_get (&src_template);
452   else
453     g_assert_not_reached ();
454
455   pad =
456       g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
457       direction, "template", template, NULL);
458   gst_object_unref (template);
459
460   gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
461   gst_pad_set_query_function (GST_PAD (pad), gst_webrtcbin_sink_query);
462
463   GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
464       direction == GST_PAD_SRC ? "src" : "sink");
465   return pad;
466 }
467
468 #define gst_webrtc_bin_parent_class parent_class
469 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
470     G_ADD_PRIVATE (GstWebRTCBin)
471     GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
472         "webrtcbin element"););
473
474 enum
475 {
476   SIGNAL_0,
477   CREATE_OFFER_SIGNAL,
478   CREATE_ANSWER_SIGNAL,
479   SET_LOCAL_DESCRIPTION_SIGNAL,
480   SET_REMOTE_DESCRIPTION_SIGNAL,
481   ADD_ICE_CANDIDATE_SIGNAL,
482   ON_NEGOTIATION_NEEDED_SIGNAL,
483   ON_ICE_CANDIDATE_SIGNAL,
484   ON_NEW_TRANSCEIVER_SIGNAL,
485   GET_STATS_SIGNAL,
486   ADD_TRANSCEIVER_SIGNAL,
487   GET_TRANSCEIVER_SIGNAL,
488   GET_TRANSCEIVERS_SIGNAL,
489   ADD_TURN_SERVER_SIGNAL,
490   CREATE_DATA_CHANNEL_SIGNAL,
491   ON_DATA_CHANNEL_SIGNAL,
492   LAST_SIGNAL,
493 };
494
495 enum
496 {
497   PROP_0,
498   PROP_CONNECTION_STATE,
499   PROP_SIGNALING_STATE,
500   PROP_ICE_GATHERING_STATE,
501   PROP_ICE_CONNECTION_STATE,
502   PROP_LOCAL_DESCRIPTION,
503   PROP_CURRENT_LOCAL_DESCRIPTION,
504   PROP_PENDING_LOCAL_DESCRIPTION,
505   PROP_REMOTE_DESCRIPTION,
506   PROP_CURRENT_REMOTE_DESCRIPTION,
507   PROP_PENDING_REMOTE_DESCRIPTION,
508   PROP_STUN_SERVER,
509   PROP_TURN_SERVER,
510   PROP_BUNDLE_POLICY,
511   PROP_ICE_TRANSPORT_POLICY,
512   PROP_ICE_AGENT,
513   PROP_LATENCY,
514   PROP_SCTP_TRANSPORT,
515 };
516
517 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
518
519 typedef struct
520 {
521   guint session_id;
522   GstWebRTCICEStream *stream;
523 } IceStreamItem;
524
525 /* FIXME: locking? */
526 GstWebRTCICEStream *
527 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
528 {
529   int i;
530
531   for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
532     IceStreamItem *item =
533         &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
534
535     if (item->session_id == session_id) {
536       GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
537           "session %u", item->stream, session_id);
538       return item->stream;
539     }
540   }
541
542   GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
543       session_id);
544   return NULL;
545 }
546
547 void
548 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
549     GstWebRTCICEStream * stream)
550 {
551   IceStreamItem item = { session_id, stream };
552
553   GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
554       "session %u", stream, session_id);
555   g_array_append_val (webrtc->priv->ice_stream_map, item);
556 }
557
558 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
559     gconstpointer data);
560
561 static GstWebRTCRTPTransceiver *
562 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
563     FindTransceiverFunc func)
564 {
565   int i;
566
567   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
568     GstWebRTCRTPTransceiver *transceiver =
569         g_ptr_array_index (webrtc->priv->transceivers, i);
570
571     if (func (transceiver, data))
572       return transceiver;
573   }
574
575   return NULL;
576 }
577
578 static gboolean
579 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
580 {
581   return g_strcmp0 (trans->mid, mid) == 0;
582 }
583
584 static gboolean
585 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
586 {
587   if (trans->stopped)
588     return FALSE;
589
590   return trans->mline == *mline;
591 }
592
593 static GstWebRTCRTPTransceiver *
594 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
595 {
596   GstWebRTCRTPTransceiver *trans;
597
598   trans = _find_transceiver (webrtc, &mlineindex,
599       (FindTransceiverFunc) transceiver_match_for_mline);
600
601   GST_TRACE_OBJECT (webrtc,
602       "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
603       mlineindex);
604
605   return trans;
606 }
607
608 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
609     gconstpointer data);
610
611 static TransportStream *
612 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
613     FindTransportFunc func)
614 {
615   int i;
616
617   for (i = 0; i < webrtc->priv->transports->len; i++) {
618     TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
619
620     if (func (stream, data))
621       return stream;
622   }
623
624   return NULL;
625 }
626
627 static gboolean
628 match_stream_for_session (TransportStream * trans, guint * session)
629 {
630   return trans->session_id == *session;
631 }
632
633 static TransportStream *
634 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
635 {
636   TransportStream *stream;
637
638   stream = _find_transport (webrtc, &session_id,
639       (FindTransportFunc) match_stream_for_session);
640
641   GST_TRACE_OBJECT (webrtc,
642       "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
643
644   return stream;
645 }
646
647 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
648
649 static GstWebRTCBinPad *
650 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
651 {
652   GstElement *element = GST_ELEMENT (webrtc);
653   GList *l;
654
655   GST_OBJECT_LOCK (webrtc);
656   l = element->pads;
657   for (; l; l = g_list_next (l)) {
658     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
659       continue;
660     if (func (l->data, data)) {
661       gst_object_ref (l->data);
662       GST_OBJECT_UNLOCK (webrtc);
663       return l->data;
664     }
665   }
666
667   l = webrtc->priv->pending_pads;
668   for (; l; l = g_list_next (l)) {
669     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
670       continue;
671     if (func (l->data, data)) {
672       gst_object_ref (l->data);
673       GST_OBJECT_UNLOCK (webrtc);
674       return l->data;
675     }
676   }
677   GST_OBJECT_UNLOCK (webrtc);
678
679   return NULL;
680 }
681
682 typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1,
683     gconstpointer data);
684
685 static WebRTCDataChannel *
686 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
687     FindDataChannelFunc func)
688 {
689   int i;
690
691   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
692     WebRTCDataChannel *channel =
693         g_ptr_array_index (webrtc->priv->data_channels, i);
694
695     if (func (channel, data))
696       return channel;
697   }
698
699   return NULL;
700 }
701
702 static gboolean
703 data_channel_match_for_id (WebRTCDataChannel * channel, gint * id)
704 {
705   return channel->parent.id == *id;
706 }
707
708 /* always called with dc_lock held */
709 static WebRTCDataChannel *
710 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
711 {
712   WebRTCDataChannel *channel;
713
714   channel = _find_data_channel (webrtc, &id,
715       (FindDataChannelFunc) data_channel_match_for_id);
716
717   GST_TRACE_OBJECT (webrtc,
718       "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
719
720   return channel;
721 }
722
723 static void
724 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
725 {
726   GST_OBJECT_LOCK (webrtc);
727   webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
728   GST_OBJECT_UNLOCK (webrtc);
729 }
730
731 static void
732 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
733 {
734   GST_OBJECT_LOCK (webrtc);
735   webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
736   GST_OBJECT_UNLOCK (webrtc);
737 }
738
739 static void
740 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
741 {
742   _remove_pending_pad (webrtc, pad);
743
744   if (webrtc->priv->running)
745     gst_pad_set_active (GST_PAD (pad), TRUE);
746   gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
747 }
748
749 static void
750 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
751 {
752   _remove_pending_pad (webrtc, pad);
753
754   gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
755 }
756
757 typedef struct
758 {
759   GstPadDirection direction;
760   guint mline;
761 } MLineMatch;
762
763 static gboolean
764 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
765 {
766   return GST_PAD_DIRECTION (pad) == match->direction
767       && pad->trans->mline == match->mline;
768 }
769
770 static GstWebRTCBinPad *
771 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
772     guint mline)
773 {
774   MLineMatch m = { direction, mline };
775
776   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
777 }
778
779 typedef struct
780 {
781   GstPadDirection direction;
782   GstWebRTCRTPTransceiver *trans;
783 } TransMatch;
784
785 static gboolean
786 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
787 {
788   return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
789 }
790
791 static GstWebRTCBinPad *
792 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
793     GstWebRTCRTPTransceiver * trans)
794 {
795   TransMatch m = { direction, trans };
796
797   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
798 }
799
800 #if 0
801 static gboolean
802 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
803 {
804   return pad->ssrc == *ssrc;
805 }
806
807 static gboolean
808 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
809 {
810   return pad == other;
811 }
812 #endif
813
814 static gboolean
815 _unlock_pc_thread (GMutex * lock)
816 {
817   g_mutex_unlock (lock);
818   return G_SOURCE_REMOVE;
819 }
820
821 static gpointer
822 _gst_pc_thread (GstWebRTCBin * webrtc)
823 {
824   PC_LOCK (webrtc);
825   webrtc->priv->main_context = g_main_context_new ();
826   webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
827
828   PC_COND_BROADCAST (webrtc);
829   g_main_context_invoke (webrtc->priv->main_context,
830       (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
831
832   /* Having the thread be the thread default GMainContext will break the
833    * required queue-like ordering (from W3's peerconnection spec) of re-entrant
834    * tasks */
835   g_main_loop_run (webrtc->priv->loop);
836
837   GST_OBJECT_LOCK (webrtc);
838   g_main_context_unref (webrtc->priv->main_context);
839   webrtc->priv->main_context = NULL;
840   GST_OBJECT_UNLOCK (webrtc);
841
842   PC_LOCK (webrtc);
843   g_main_loop_unref (webrtc->priv->loop);
844   webrtc->priv->loop = NULL;
845   PC_COND_BROADCAST (webrtc);
846   PC_UNLOCK (webrtc);
847
848   return NULL;
849 }
850
851 static void
852 _start_thread (GstWebRTCBin * webrtc)
853 {
854   gchar *name;
855
856   PC_LOCK (webrtc);
857   name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc));
858   webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread,
859       webrtc);
860   g_free (name);
861
862   while (!webrtc->priv->loop)
863     PC_COND_WAIT (webrtc);
864   webrtc->priv->is_closed = FALSE;
865   PC_UNLOCK (webrtc);
866 }
867
868 static void
869 _stop_thread (GstWebRTCBin * webrtc)
870 {
871   GST_OBJECT_LOCK (webrtc);
872   webrtc->priv->is_closed = TRUE;
873   GST_OBJECT_UNLOCK (webrtc);
874
875   PC_LOCK (webrtc);
876   g_main_loop_quit (webrtc->priv->loop);
877   while (webrtc->priv->loop)
878     PC_COND_WAIT (webrtc);
879   PC_UNLOCK (webrtc);
880
881   g_thread_unref (webrtc->priv->thread);
882 }
883
884 static gboolean
885 _execute_op (GstWebRTCBinTask * op)
886 {
887   GstStructure *s;
888
889   PC_LOCK (op->webrtc);
890   if (op->webrtc->priv->is_closed) {
891     PC_UNLOCK (op->webrtc);
892
893     if (op->promise) {
894       GError *error =
895           g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
896           "webrtcbin is closed. aborting execution.");
897       GstStructure *s = gst_structure_new ("application/x-gst-promise",
898           "error", G_TYPE_ERROR, error, NULL);
899
900       gst_promise_reply (op->promise, s);
901
902       g_clear_error (&error);
903     }
904     GST_DEBUG_OBJECT (op->webrtc,
905         "Peerconnection is closed, aborting execution");
906     goto out;
907   }
908
909   s = op->op (op->webrtc, op->data);
910
911   PC_UNLOCK (op->webrtc);
912
913   if (op->promise)
914     gst_promise_reply (op->promise, s);
915   else if (s)
916     gst_structure_free (s);
917
918 out:
919   return G_SOURCE_REMOVE;
920 }
921
922 static void
923 _free_op (GstWebRTCBinTask * op)
924 {
925   if (op->notify)
926     op->notify (op->data);
927   if (op->promise)
928     gst_promise_unref (op->promise);
929   g_free (op);
930 }
931
932 /*
933  * @promise is for correctly signalling the failure case to the caller when
934  * the user supplies it.  Without passing it in, the promise would never
935  * be replied to in the case that @webrtc becomes closed between the idle
936  * source addition and the the execution of the idle source.
937  */
938 gboolean
939 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
940     gpointer data, GDestroyNotify notify, GstPromise * promise)
941 {
942   GstWebRTCBinTask *op;
943   GMainContext *ctx;
944   GSource *source;
945
946   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
947
948   GST_OBJECT_LOCK (webrtc);
949   if (webrtc->priv->is_closed) {
950     GST_OBJECT_UNLOCK (webrtc);
951     GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
952     if (notify)
953       notify (data);
954     return FALSE;
955   }
956   ctx = g_main_context_ref (webrtc->priv->main_context);
957   GST_OBJECT_UNLOCK (webrtc);
958
959   op = g_new0 (GstWebRTCBinTask, 1);
960   op->webrtc = webrtc;
961   op->op = func;
962   op->data = data;
963   op->notify = notify;
964   if (promise)
965     op->promise = gst_promise_ref (promise);
966
967   source = g_idle_source_new ();
968   g_source_set_priority (source, G_PRIORITY_DEFAULT);
969   g_source_set_callback (source, (GSourceFunc) _execute_op, op,
970       (GDestroyNotify) _free_op);
971   g_source_attach (source, ctx);
972   g_source_unref (source);
973   g_main_context_unref (ctx);
974
975   return TRUE;
976 }
977
978 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
979 static GstWebRTCICEConnectionState
980 _collate_ice_connection_states (GstWebRTCBin * webrtc)
981 {
982 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
983   GstWebRTCICEConnectionState any_state = 0;
984   gboolean all_new_or_closed = TRUE;
985   gboolean all_completed_or_closed = TRUE;
986   gboolean all_connected_completed_or_closed = TRUE;
987   int i;
988
989   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
990     GstWebRTCRTPTransceiver *rtp_trans =
991         g_ptr_array_index (webrtc->priv->transceivers, i);
992     GstWebRTCICETransport *transport;
993     GstWebRTCICEConnectionState ice_state;
994
995     if (rtp_trans->stopped) {
996       GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
997       continue;
998     }
999
1000     if (!rtp_trans->mid) {
1001       GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1002       continue;
1003     }
1004
1005     transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
1006
1007     /* get transport state */
1008     g_object_get (transport, "state", &ice_state, NULL);
1009     GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans,
1010         ice_state);
1011     any_state |= (1 << ice_state);
1012
1013     if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED))
1014       all_new_or_closed = FALSE;
1015     if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED))
1016       all_completed_or_closed = FALSE;
1017     if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED)
1018         && ice_state != STATE (CLOSED))
1019       all_connected_completed_or_closed = FALSE;
1020   }
1021
1022   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
1023
1024   if (webrtc->priv->is_closed) {
1025     GST_TRACE_OBJECT (webrtc, "returning closed");
1026     return STATE (CLOSED);
1027   }
1028   /* Any of the RTCIceTransports are in the failed state. */
1029   if (any_state & (1 << STATE (FAILED))) {
1030     GST_TRACE_OBJECT (webrtc, "returning failed");
1031     return STATE (FAILED);
1032   }
1033   /* Any of the RTCIceTransports are in the disconnected state. */
1034   if (any_state & (1 << STATE (DISCONNECTED))) {
1035     GST_TRACE_OBJECT (webrtc, "returning disconnected");
1036     return STATE (DISCONNECTED);
1037   }
1038   /* All of the RTCIceTransports are in the new or closed state, or there are
1039    * no transports. */
1040   if (all_new_or_closed || webrtc->priv->transceivers->len == 0) {
1041     GST_TRACE_OBJECT (webrtc, "returning new");
1042     return STATE (NEW);
1043   }
1044   /* Any of the RTCIceTransports are in the checking or new state. */
1045   if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) {
1046     GST_TRACE_OBJECT (webrtc, "returning checking");
1047     return STATE (CHECKING);
1048   }
1049   /* All RTCIceTransports are in the completed or closed state. */
1050   if (all_completed_or_closed) {
1051     GST_TRACE_OBJECT (webrtc, "returning completed");
1052     return STATE (COMPLETED);
1053   }
1054   /* All RTCIceTransports are in the connected, completed or closed state. */
1055   if (all_connected_completed_or_closed) {
1056     GST_TRACE_OBJECT (webrtc, "returning connected");
1057     return STATE (CONNECTED);
1058   }
1059
1060   GST_FIXME ("unspecified situation, returning old state");
1061   return webrtc->ice_connection_state;
1062 #undef STATE
1063 }
1064
1065 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
1066 static GstWebRTCICEGatheringState
1067 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
1068 {
1069 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
1070   GstWebRTCICEGatheringState any_state = 0;
1071   gboolean all_completed = webrtc->priv->transceivers->len > 0;
1072   int i;
1073
1074   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1075     GstWebRTCRTPTransceiver *rtp_trans =
1076         g_ptr_array_index (webrtc->priv->transceivers, i);
1077     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1078     TransportStream *stream = trans->stream;
1079     GstWebRTCDTLSTransport *dtls_transport;
1080     GstWebRTCICETransport *transport;
1081     GstWebRTCICEGatheringState ice_state;
1082
1083     if (rtp_trans->stopped || stream == NULL) {
1084       GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated",
1085           rtp_trans);
1086       continue;
1087     }
1088
1089     /* We only have a mid in the transceiver after we got the SDP answer,
1090      * which is usually long after gathering has finished */
1091     if (!rtp_trans->mid) {
1092       GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1093     }
1094
1095     dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1096     if (dtls_transport == NULL) {
1097       GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans);
1098       continue;
1099     }
1100
1101     transport = dtls_transport->transport;
1102
1103     /* get gathering state */
1104     g_object_get (transport, "gathering-state", &ice_state, NULL);
1105     GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans,
1106         ice_state);
1107     any_state |= (1 << ice_state);
1108     if (ice_state != STATE (COMPLETE))
1109       all_completed = FALSE;
1110   }
1111
1112   GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
1113
1114   /* Any of the RTCIceTransport s are in the gathering state. */
1115   if (any_state & (1 << STATE (GATHERING))) {
1116     GST_TRACE_OBJECT (webrtc, "returning gathering");
1117     return STATE (GATHERING);
1118   }
1119   /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
1120    * the completed gathering state. */
1121   if (all_completed) {
1122     GST_TRACE_OBJECT (webrtc, "returning complete");
1123     return STATE (COMPLETE);
1124   }
1125
1126   /* Any of the RTCIceTransport s are in the new gathering state and none
1127    * of the transports are in the gathering state, or there are no transports. */
1128   GST_TRACE_OBJECT (webrtc, "returning new");
1129   return STATE (NEW);
1130 #undef STATE
1131 }
1132
1133 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
1134 static GstWebRTCPeerConnectionState
1135 _collate_peer_connection_states (GstWebRTCBin * webrtc)
1136 {
1137 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
1138 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
1139 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
1140   GstWebRTCICEConnectionState any_ice_state = 0;
1141   GstWebRTCDTLSTransportState any_dtls_state = 0;
1142   gboolean ice_all_new_or_closed = TRUE;
1143   gboolean dtls_all_new_or_closed = TRUE;
1144   gboolean ice_all_new_connecting_or_checking = TRUE;
1145   gboolean dtls_all_new_connecting_or_checking = TRUE;
1146   gboolean ice_all_connected_completed_or_closed = TRUE;
1147   gboolean dtls_all_connected_completed_or_closed = TRUE;
1148   int i;
1149
1150   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1151     GstWebRTCRTPTransceiver *rtp_trans =
1152         g_ptr_array_index (webrtc->priv->transceivers, i);
1153     GstWebRTCDTLSTransport *transport;
1154     GstWebRTCICEConnectionState ice_state;
1155     GstWebRTCDTLSTransportState dtls_state;
1156
1157     if (rtp_trans->stopped) {
1158       GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans);
1159       continue;
1160     }
1161     if (!rtp_trans->mid) {
1162       GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans);
1163       continue;
1164     }
1165
1166     transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
1167
1168     /* get transport state */
1169     g_object_get (transport, "state", &dtls_state, NULL);
1170     GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans,
1171         dtls_state);
1172     any_dtls_state |= (1 << dtls_state);
1173
1174     if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1175       dtls_all_new_or_closed = FALSE;
1176     if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1177       dtls_all_new_connecting_or_checking = FALSE;
1178     if (dtls_state != DTLS_STATE (CONNECTED)
1179         && dtls_state != DTLS_STATE (CLOSED))
1180       dtls_all_connected_completed_or_closed = FALSE;
1181
1182     g_object_get (transport->transport, "state", &ice_state, NULL);
1183     GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans,
1184         ice_state);
1185     any_ice_state |= (1 << ice_state);
1186
1187     if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1188       ice_all_new_or_closed = FALSE;
1189     if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1190       ice_all_new_connecting_or_checking = FALSE;
1191     if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1192         && ice_state != ICE_STATE (CLOSED))
1193       ice_all_connected_completed_or_closed = FALSE;
1194   }
1195
1196   // also check data channel transport state
1197   if (webrtc->priv->data_channel_transport) {
1198     GstWebRTCDTLSTransport *transport =
1199         webrtc->priv->data_channel_transport->transport;
1200     GstWebRTCICEConnectionState ice_state;
1201     GstWebRTCDTLSTransportState dtls_state;
1202
1203     g_object_get (transport, "state", &dtls_state, NULL);
1204     GST_TRACE_OBJECT (webrtc, "data channel transport DTLS state: 0x%x",
1205         dtls_state);
1206     any_dtls_state |= (1 << dtls_state);
1207
1208     if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED))
1209       dtls_all_new_or_closed = FALSE;
1210     if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING))
1211       dtls_all_new_connecting_or_checking = FALSE;
1212     if (dtls_state != DTLS_STATE (CONNECTED)
1213         && dtls_state != DTLS_STATE (CLOSED))
1214       dtls_all_connected_completed_or_closed = FALSE;
1215
1216     g_object_get (transport->transport, "state", &ice_state, NULL);
1217     GST_TRACE_OBJECT (webrtc, "data channel transport ICE state: 0x%x",
1218         ice_state);
1219     any_ice_state |= (1 << ice_state);
1220
1221     if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED))
1222       ice_all_new_or_closed = FALSE;
1223     if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING))
1224       ice_all_new_connecting_or_checking = FALSE;
1225     if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED)
1226         && ice_state != ICE_STATE (CLOSED))
1227       ice_all_connected_completed_or_closed = FALSE;
1228   }
1229
1230   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
1231       "state: 0x%x", any_ice_state, any_dtls_state);
1232
1233   /* The RTCPeerConnection object's [[ isClosed]] slot is true.  */
1234   if (webrtc->priv->is_closed) {
1235     GST_TRACE_OBJECT (webrtc, "returning closed");
1236     return STATE (CLOSED);
1237   }
1238
1239   /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
1240   if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1241     GST_TRACE_OBJECT (webrtc, "returning failed");
1242     return STATE (FAILED);
1243   }
1244   if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1245     GST_TRACE_OBJECT (webrtc, "returning failed");
1246     return STATE (FAILED);
1247   }
1248
1249   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1250    * state. */
1251   if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1252     GST_TRACE_OBJECT (webrtc, "returning disconnected");
1253     return STATE (DISCONNECTED);
1254   }
1255
1256   /* All RTCIceTransports and RTCDtlsTransports are in the new or closed
1257    * state, or there are no transports. */
1258   if ((dtls_all_new_or_closed && ice_all_new_or_closed)
1259       || webrtc->priv->transports->len == 0) {
1260     GST_TRACE_OBJECT (webrtc, "returning new");
1261     return STATE (NEW);
1262   }
1263
1264   /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting
1265    * or checking state. */
1266   if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) {
1267     GST_TRACE_OBJECT (webrtc, "returning connecting");
1268     return STATE (CONNECTING);
1269   }
1270
1271   /* All RTCIceTransports and RTCDtlsTransports are in the connected,
1272    * completed or closed state. */
1273   if (dtls_all_connected_completed_or_closed
1274       && ice_all_connected_completed_or_closed) {
1275     GST_TRACE_OBJECT (webrtc, "returning connected");
1276     return STATE (CONNECTED);
1277   }
1278
1279   /* FIXME: Unspecified state that happens for us */
1280   if ((dtls_all_new_connecting_or_checking
1281           || dtls_all_connected_completed_or_closed)
1282       && (ice_all_new_connecting_or_checking
1283           || ice_all_connected_completed_or_closed)) {
1284     GST_TRACE_OBJECT (webrtc, "returning connecting");
1285     return STATE (CONNECTING);
1286   }
1287
1288   GST_FIXME_OBJECT (webrtc,
1289       "Undefined situation detected, returning old state");
1290   return webrtc->peer_connection_state;
1291 #undef DTLS_STATE
1292 #undef ICE_STATE
1293 #undef STATE
1294 }
1295
1296 static GstStructure *
1297 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1298 {
1299   GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1300   GstWebRTCICEGatheringState new_state;
1301
1302   new_state = _collate_ice_gathering_states (webrtc);
1303
1304   /* If the new state is complete, before we update the public state,
1305    * check if anyone published more ICE candidates while we were collating
1306    * and stop if so, because it means there's a new later
1307    * ice_gathering_state_task queued */
1308   if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) {
1309     ICE_LOCK (webrtc);
1310     if (webrtc->priv->pending_local_ice_candidates->len != 0) {
1311       /* ICE candidates queued for emissiong -> we're gathering, not complete */
1312       new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING;
1313     }
1314     ICE_UNLOCK (webrtc);
1315   }
1316
1317   if (new_state != webrtc->ice_gathering_state) {
1318     gchar *old_s, *new_s;
1319
1320     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1321         old_state);
1322     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1323         new_state);
1324     GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1325         old_s, old_state, new_s, new_state);
1326     g_free (old_s);
1327     g_free (new_s);
1328
1329     webrtc->ice_gathering_state = new_state;
1330     PC_UNLOCK (webrtc);
1331     g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1332     PC_LOCK (webrtc);
1333   }
1334
1335   return NULL;
1336 }
1337
1338 static void
1339 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1340 {
1341   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1342       NULL, NULL);
1343 }
1344
1345 static GstStructure *
1346 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1347 {
1348   GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1349   GstWebRTCICEConnectionState new_state;
1350
1351   new_state = _collate_ice_connection_states (webrtc);
1352
1353   if (new_state != old_state) {
1354     gchar *old_s, *new_s;
1355
1356     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1357         old_state);
1358     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1359         new_state);
1360     GST_INFO_OBJECT (webrtc,
1361         "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1362         new_s, new_state);
1363     g_free (old_s);
1364     g_free (new_s);
1365
1366     webrtc->ice_connection_state = new_state;
1367     PC_UNLOCK (webrtc);
1368     g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1369     PC_LOCK (webrtc);
1370   }
1371
1372   return NULL;
1373 }
1374
1375 static void
1376 _update_ice_connection_state (GstWebRTCBin * webrtc)
1377 {
1378   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1379       NULL, NULL);
1380 }
1381
1382 static GstStructure *
1383 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1384 {
1385   GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1386   GstWebRTCPeerConnectionState new_state;
1387
1388   new_state = _collate_peer_connection_states (webrtc);
1389
1390   if (new_state != old_state) {
1391     gchar *old_s, *new_s;
1392
1393     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1394         old_state);
1395     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1396         new_state);
1397     GST_INFO_OBJECT (webrtc,
1398         "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1399         new_s, new_state);
1400     g_free (old_s);
1401     g_free (new_s);
1402
1403     webrtc->peer_connection_state = new_state;
1404     PC_UNLOCK (webrtc);
1405     g_object_notify (G_OBJECT (webrtc), "connection-state");
1406     PC_LOCK (webrtc);
1407   }
1408
1409   return NULL;
1410 }
1411
1412 static void
1413 _update_peer_connection_state (GstWebRTCBin * webrtc)
1414 {
1415   gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1416       NULL, NULL, NULL);
1417 }
1418
1419 static gboolean
1420 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1421 {
1422   GList *l;
1423   gboolean res = FALSE;
1424
1425   GST_OBJECT_LOCK (webrtc);
1426   l = GST_ELEMENT (webrtc)->pads;
1427   for (; l; l = g_list_next (l)) {
1428     GstWebRTCBinPad *wpad;
1429
1430     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1431       continue;
1432
1433     wpad = GST_WEBRTC_BIN_PAD (l->data);
1434     if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1435         && (!wpad->trans || !wpad->trans->stopped)) {
1436       if (wpad->trans && wpad->trans->codec_preferences) {
1437         continue;
1438       } else {
1439         goto done;
1440       }
1441     }
1442   }
1443
1444   l = webrtc->priv->pending_pads;
1445   for (; l; l = g_list_next (l)) {
1446     if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1447       goto done;
1448     }
1449   }
1450
1451   res = TRUE;
1452
1453 done:
1454   GST_OBJECT_UNLOCK (webrtc);
1455   return res;
1456 }
1457
1458 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1459 static gboolean
1460 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1461 {
1462   int i;
1463
1464   GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1465
1466   /* We can't negotiate until we have received caps on all our sink pads,
1467    * as we will need the ssrcs in our offer / answer */
1468   if (!_all_sinks_have_caps (webrtc)) {
1469     GST_LOG_OBJECT (webrtc,
1470         "no negotiation possible until caps have been received on all sink pads");
1471     return FALSE;
1472   }
1473
1474   /* If any implementation-specific negotiation is required, as described at
1475    * the start of this section, return "true".
1476    * FIXME */
1477   /* FIXME: emit when input caps/format changes? */
1478
1479   if (!webrtc->current_local_description) {
1480     GST_LOG_OBJECT (webrtc, "no local description set");
1481     return TRUE;
1482   }
1483
1484   if (!webrtc->current_remote_description) {
1485     GST_LOG_OBJECT (webrtc, "no remote description set");
1486     return TRUE;
1487   }
1488
1489   /* If connection has created any RTCDataChannel's, and no m= section has
1490    * been negotiated yet for data, return "true". */
1491   if (webrtc->priv->data_channels->len > 0) {
1492     if (_message_get_datachannel_index (webrtc->current_local_description->
1493             sdp) >= G_MAXUINT) {
1494       GST_LOG_OBJECT (webrtc,
1495           "no data channel media section and have %u " "transports",
1496           webrtc->priv->data_channels->len);
1497       return TRUE;
1498     }
1499   }
1500
1501   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1502     GstWebRTCRTPTransceiver *trans;
1503
1504     trans = g_ptr_array_index (webrtc->priv->transceivers, i);
1505
1506     if (trans->stopped) {
1507       /* FIXME: If t is stopped and is associated with an m= section according to
1508        * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1509        * rejected in connection's currentLocalDescription or
1510        * currentRemoteDescription , return "true". */
1511       GST_FIXME_OBJECT (webrtc,
1512           "check if the transceiver is rejected in descriptions");
1513     } else {
1514       const GstSDPMedia *media;
1515       GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1516
1517       if (trans->mline == -1 || trans->mid == NULL) {
1518         GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1519             " mid %s", i, trans, trans->mid);
1520         return TRUE;
1521       }
1522       /* internal inconsistency */
1523       g_assert (trans->mline <
1524           gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1525       g_assert (trans->mline <
1526           gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1527
1528       /* FIXME: msid handling
1529        * If t's direction is "sendrecv" or "sendonly", and the associated m=
1530        * section in connection's currentLocalDescription doesn't contain an
1531        * "a=msid" line, return "true". */
1532
1533       media =
1534           gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1535           trans->mline);
1536       local_dir = _get_direction_from_media (media);
1537
1538       media =
1539           gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1540           trans->mline);
1541       remote_dir = _get_direction_from_media (media);
1542
1543       if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1544         /* If connection's currentLocalDescription if of type "offer", and
1545          * the direction of the associated m= section in neither the offer
1546          * nor answer matches t's direction, return "true". */
1547
1548         if (local_dir != trans->direction && remote_dir != trans->direction) {
1549           gchar *local_str, *remote_str, *dir_str;
1550
1551           local_str =
1552               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1553               local_dir);
1554           remote_str =
1555               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1556               remote_dir);
1557           dir_str =
1558               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1559               trans->direction);
1560
1561           GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1562               "description (local %s remote %s)", dir_str, local_str,
1563               remote_str);
1564
1565           g_free (dir_str);
1566           g_free (local_str);
1567           g_free (remote_str);
1568
1569           return TRUE;
1570         }
1571       } else if (webrtc->current_local_description->type ==
1572           GST_WEBRTC_SDP_TYPE_ANSWER) {
1573         GstWebRTCRTPTransceiverDirection intersect_dir;
1574
1575         /* If connection's currentLocalDescription if of type "answer", and
1576          * the direction of the associated m= section in the answer does not
1577          * match t's direction intersected with the offered direction (as
1578          * described in [JSEP] (section 5.3.1.)), return "true". */
1579
1580         /* remote is the offer, local is the answer */
1581         intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1582
1583         if (intersect_dir != trans->direction) {
1584           gchar *local_str, *remote_str, *inter_str, *dir_str;
1585
1586           local_str =
1587               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1588               local_dir);
1589           remote_str =
1590               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1591               remote_dir);
1592           dir_str =
1593               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1594               trans->direction);
1595           inter_str =
1596               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1597               intersect_dir);
1598
1599           GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1600               "description intersected direction %s (local %s remote %s)",
1601               dir_str, local_str, inter_str, remote_str);
1602
1603           g_free (dir_str);
1604           g_free (local_str);
1605           g_free (remote_str);
1606           g_free (inter_str);
1607
1608           return TRUE;
1609         }
1610       }
1611     }
1612   }
1613
1614   GST_LOG_OBJECT (webrtc, "no negotiation needed");
1615   return FALSE;
1616 }
1617
1618 static GstStructure *
1619 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1620 {
1621   if (webrtc->priv->need_negotiation) {
1622     GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1623     PC_UNLOCK (webrtc);
1624     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1625         0);
1626     PC_LOCK (webrtc);
1627   }
1628
1629   return NULL;
1630 }
1631
1632 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1633 static void
1634 _update_need_negotiation (GstWebRTCBin * webrtc)
1635 {
1636   /* If connection's [[isClosed]] slot is true, abort these steps. */
1637   if (webrtc->priv->is_closed)
1638     return;
1639   /* If connection's signaling state is not "stable", abort these steps. */
1640   if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1641     return;
1642
1643   /* If the result of checking if negotiation is needed is "false", clear the
1644    * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1645    * to false, and abort these steps. */
1646   if (!_check_if_negotiation_is_needed (webrtc)) {
1647     webrtc->priv->need_negotiation = FALSE;
1648     return;
1649   }
1650   /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1651   if (webrtc->priv->need_negotiation)
1652     return;
1653   /* Set connection's [[needNegotiation]] slot to true. */
1654   webrtc->priv->need_negotiation = TRUE;
1655   /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1656    * true, fire a simple event named negotiationneeded at connection. */
1657   gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1658       NULL, NULL);
1659 }
1660
1661 static GstCaps *
1662 _query_pad_caps (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * rtp_trans,
1663     GstWebRTCBinPad * pad, GstCaps * filter, GError ** error)
1664 {
1665   GstCaps *caps;
1666   guint i, n;
1667
1668   caps = gst_pad_peer_query_caps (GST_PAD (pad), filter);
1669   GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps);
1670
1671   /* Only return an error if actual empty caps were returned from the query. */
1672   if (gst_caps_is_empty (caps)) {
1673     g_set_error (error, GST_WEBRTC_BIN_ERROR,
1674         GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
1675         "Caps negotiation on pad %s failed", GST_PAD_NAME (pad));
1676     gst_clear_caps (&caps);
1677     gst_caps_unref (filter);
1678     return NULL;
1679   }
1680
1681   n = gst_caps_get_size (caps);
1682   if (n > 0) {
1683     /* Make sure the caps are complete enough to figure out the media type and
1684      * encoding-name, otherwise they would match with basically any media. */
1685     caps = gst_caps_make_writable (caps);
1686     for (i = n; i > 0; i--) {
1687       const GstStructure *s = gst_caps_get_structure (caps, i - 1);
1688
1689       if (!gst_structure_has_name (s, "application/x-rtp") ||
1690           !gst_structure_has_field (s, "media") ||
1691           !gst_structure_has_field (s, "encoding-name")) {
1692         gst_caps_remove_structure (caps, i - 1);
1693       }
1694     }
1695   }
1696
1697   /* If the filtering above resulted in empty caps, or the caps were ANY to
1698    * begin with, then don't report and error but just NULL.
1699    *
1700    * This would be the case if negotiation would not fail but the peer does
1701    * not have any specific enough preferred caps that would allow us to
1702    * use them further.
1703    */
1704   if (gst_caps_is_any (caps) || gst_caps_is_empty (caps)) {
1705     GST_DEBUG_OBJECT (webrtc, "Peer caps not specific enough");
1706     gst_clear_caps (&caps);
1707   }
1708
1709   gst_caps_unref (filter);
1710
1711   return caps;
1712 }
1713
1714 static GstCaps *
1715 _find_codec_preferences (GstWebRTCBin * webrtc,
1716     GstWebRTCRTPTransceiver * rtp_trans, guint media_idx, GError ** error)
1717 {
1718   WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1719   GstCaps *ret = NULL;
1720   GstCaps *codec_preferences = NULL;
1721   GstWebRTCBinPad *pad = NULL;
1722   GstPadDirection direction;
1723
1724   g_assert (rtp_trans);
1725   g_assert (error && *error == NULL);
1726
1727   GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT,
1728       trans);
1729
1730   GST_OBJECT_LOCK (rtp_trans);
1731   if (rtp_trans->codec_preferences) {
1732     GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1733         rtp_trans->codec_preferences);
1734     codec_preferences = gst_caps_ref (rtp_trans->codec_preferences);
1735   }
1736   GST_OBJECT_UNLOCK (rtp_trans);
1737
1738   if (rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
1739     direction = GST_PAD_SRC;
1740   else
1741     direction = GST_PAD_SINK;
1742
1743   pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans);
1744
1745   /* try to find a pad */
1746   if (!pad)
1747     pad = _find_pad_for_mline (webrtc, direction, media_idx);
1748
1749   /* For the case where we have set our transceiver to sendrecv, but the
1750    * sink pad has not been requested yet.
1751    */
1752   if (!pad &&
1753       rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1754
1755     pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1756
1757     /* try to find a pad */
1758     if (!pad)
1759       pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
1760   }
1761
1762   if (pad) {
1763     GstCaps *caps = NULL;
1764
1765     if (pad->received_caps) {
1766       caps = gst_caps_ref (pad->received_caps);
1767     } else {
1768       static GstStaticCaps static_filter =
1769           GST_STATIC_CAPS ("application/x-rtp, "
1770           "media = (string) { audio, video }, payload = (int) [ 0, 127 ]");
1771       GstCaps *filter = gst_static_caps_get (&static_filter);
1772
1773       filter = gst_caps_make_writable (filter);
1774
1775       if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
1776         gst_caps_set_simple (filter, "media", G_TYPE_STRING, "audio", NULL);
1777       else if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
1778         gst_caps_set_simple (filter, "media", G_TYPE_STRING, "video", NULL);
1779
1780       caps = _query_pad_caps (webrtc, rtp_trans, pad, filter, error);
1781     }
1782     gst_object_unref (pad);
1783
1784     if (*error)
1785       goto out;
1786
1787     if (caps &&
1788         rtp_trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
1789       GstWebRTCBinPad *srcpad =
1790           _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
1791
1792       if (srcpad) {
1793         caps = _query_pad_caps (webrtc, rtp_trans, srcpad, caps, error);
1794         gst_object_unref (srcpad);
1795
1796         if (*error)
1797           goto out;
1798       }
1799     }
1800
1801     if (caps && codec_preferences) {
1802       GstCaps *intersection;
1803
1804       intersection = gst_caps_intersect_full (codec_preferences, caps,
1805           GST_CAPS_INTERSECT_FIRST);
1806       gst_clear_caps (&caps);
1807
1808       if (gst_caps_is_empty (intersection)) {
1809         g_set_error (error, GST_WEBRTC_BIN_ERROR,
1810             GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
1811             "Caps negotiation on pad %s failed againt codec preferences",
1812             GST_PAD_NAME (pad));
1813         gst_clear_caps (&intersection);
1814       } else {
1815         caps = intersection;
1816       }
1817     }
1818
1819     if (caps) {
1820       if (trans)
1821         gst_caps_replace (&trans->last_configured_caps, caps);
1822
1823       ret = caps;
1824     }
1825   }
1826
1827   if (!ret) {
1828     if (codec_preferences)
1829       ret = gst_caps_ref (codec_preferences);
1830     else if (trans->last_configured_caps)
1831       ret = gst_caps_ref (trans->last_configured_caps);
1832   }
1833
1834 out:
1835
1836   if (codec_preferences)
1837     gst_caps_unref (codec_preferences);
1838
1839   if (!ret)
1840     GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1841
1842   return ret;
1843 }
1844
1845 static GstCaps *
1846 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1847     WebRTCTransceiver * trans, const GstCaps * caps)
1848 {
1849   GstWebRTCKind kind;
1850   GstCaps *ret;
1851   guint i;
1852
1853   if (caps == NULL)
1854     return NULL;
1855
1856   ret = gst_caps_make_writable (caps);
1857
1858   kind = webrtc_kind_from_caps (ret);
1859   for (i = 0; i < gst_caps_get_size (ret); i++) {
1860     GstStructure *s = gst_caps_get_structure (ret, i);
1861
1862     if (trans->do_nack)
1863       if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1864         gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1865
1866     if (kind == GST_WEBRTC_KIND_VIDEO
1867         && !gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1868       gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1869     if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1870       gst_structure_set (s, "rtcp-fb-transport-cc", G_TYPE_BOOLEAN, TRUE, NULL);
1871
1872     /* FIXME: codec-specific parameters? */
1873   }
1874
1875   return ret;
1876 }
1877
1878 static void
1879 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1880     GParamSpec * pspec, GstWebRTCBin * webrtc)
1881 {
1882   _update_ice_connection_state (webrtc);
1883   _update_peer_connection_state (webrtc);
1884 }
1885
1886 static void
1887 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1888     GParamSpec * pspec, GstWebRTCBin * webrtc)
1889 {
1890   _update_ice_gathering_state (webrtc);
1891 }
1892
1893 static void
1894 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1895     GParamSpec * pspec, GstWebRTCBin * webrtc)
1896 {
1897   _update_peer_connection_state (webrtc);
1898 }
1899
1900 static gboolean
1901 match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
1902 {
1903   WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1904
1905   return (trans->current_ssrc == GPOINTER_TO_UINT (data));
1906 }
1907
1908 static gboolean
1909 _on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
1910     gboolean early, gpointer user_data)
1911 {
1912   GstWebRTCBin *webrtc = user_data;
1913   GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
1914   GstRTCPPacket packet;
1915
1916   if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
1917     goto done;
1918
1919   if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
1920     if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
1921       guint32 ssrc;
1922       GstWebRTCRTPTransceiver *rtp_trans;
1923       WebRTCTransceiver *trans;
1924
1925       gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
1926           NULL);
1927
1928       rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1929           match_ssrc);
1930       trans = (WebRTCTransceiver *) rtp_trans;
1931
1932       if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
1933         GstPad *pad;
1934         gchar *pad_name = NULL;
1935
1936         pad_name =
1937             g_strdup_printf ("send_rtcp_src_%u",
1938             rtp_trans->sender->transport->session_id);
1939         pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
1940         g_free (pad_name);
1941         if (pad) {
1942           gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
1943           gst_object_unref (pad);
1944         }
1945       }
1946     }
1947   }
1948
1949   gst_rtcp_buffer_unmap (&rtcp);
1950
1951 done:
1952   /* False means we don't care about suppression */
1953   return FALSE;
1954 }
1955
1956 static void
1957 gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
1958 {
1959   GObject *internal_session = NULL;
1960
1961   g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
1962       session_id, &internal_session);
1963
1964   if (internal_session) {
1965     g_signal_connect (internal_session, "on-sending-rtcp",
1966         G_CALLBACK (_on_sending_rtcp), webrtc);
1967     g_object_unref (internal_session);
1968   }
1969 }
1970
1971 static void
1972 weak_free (GWeakRef * weak)
1973 {
1974   g_weak_ref_clear (weak);
1975   g_free (weak);
1976 }
1977
1978 static GstPadProbeReturn
1979 _nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
1980 {
1981   GstWebRTCBin *webrtc = g_weak_ref_get ((GWeakRef *) user_data);
1982
1983   if (!webrtc)
1984     return GST_PAD_PROBE_REMOVE;
1985
1986   if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
1987       == GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
1988     const GstStructure *s =
1989         gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
1990
1991     if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
1992       guint ssrc;
1993       gint priority;
1994
1995       if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1996         GstWebRTCRTPTransceiver *rtp_trans;
1997
1998         rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
1999             match_ssrc);
2000         if (rtp_trans) {
2001           WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
2002           GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
2003               trans->stream->session_id);
2004           guint8 dscp = 0;
2005
2006           /* Set DSCP field based on
2007            * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2008            */
2009           switch (rtp_trans->sender->priority) {
2010             case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2011               dscp = 8;         /* CS1 */
2012               break;
2013             case GST_WEBRTC_PRIORITY_TYPE_LOW:
2014               dscp = 0;         /* DF */
2015               break;
2016             case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2017               switch (rtp_trans->kind) {
2018                 case GST_WEBRTC_KIND_AUDIO:
2019                   dscp = 46;    /* EF */
2020                   break;
2021                 case GST_WEBRTC_KIND_VIDEO:
2022                   dscp = 38;    /* AF43 *//* TODO: differentiate non-interactive */
2023                   break;
2024                 case GST_WEBRTC_KIND_UNKNOWN:
2025                   dscp = 0;
2026                   break;
2027               }
2028               break;
2029             case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2030               switch (rtp_trans->kind) {
2031                 case GST_WEBRTC_KIND_AUDIO:
2032                   dscp = 46;    /* EF */
2033                   break;
2034                 case GST_WEBRTC_KIND_VIDEO:
2035                   dscp = 36;    /* AF42 *//* TODO: differentiate non-interactive */
2036                   break;
2037                 case GST_WEBRTC_KIND_UNKNOWN:
2038                   dscp = 0;
2039                   break;
2040               }
2041               break;
2042           }
2043
2044           gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
2045         }
2046       } else if (gst_structure_get_enum (s, "sctp-priority",
2047               GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
2048         guint8 dscp = 0;
2049
2050         /* Set DSCP field based on
2051          * https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
2052          */
2053         switch (priority) {
2054           case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
2055             dscp = 8;           /* CS1 */
2056             break;
2057           case GST_WEBRTC_PRIORITY_TYPE_LOW:
2058             dscp = 0;           /* DF */
2059             break;
2060           case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
2061             dscp = 10;          /* AF11 */
2062             break;
2063           case GST_WEBRTC_PRIORITY_TYPE_HIGH:
2064             dscp = 18;          /* AF21 */
2065             break;
2066         }
2067         if (webrtc->priv->data_channel_transport)
2068           gst_webrtc_ice_set_tos (webrtc->priv->ice,
2069               webrtc->priv->data_channel_transport->stream, dscp << 2);
2070       }
2071     }
2072   }
2073
2074   gst_object_unref (webrtc);
2075
2076   return GST_PAD_PROBE_OK;
2077 }
2078
2079 static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
2080
2081 static void
2082 gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
2083 {
2084   GstWebRTCPriorityType sctp_priority = 0;
2085   guint i;
2086
2087   if (!webrtc->priv->sctp_transport)
2088     return;
2089
2090   DC_LOCK (webrtc);
2091   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2092     GstWebRTCDataChannel *channel
2093         = g_ptr_array_index (webrtc->priv->data_channels, i);
2094
2095     sctp_priority = MAX (sctp_priority, channel->priority);
2096   }
2097   DC_UNLOCK (webrtc);
2098
2099   /* Default priority is low means DSCP field is left as 0 */
2100   if (sctp_priority == 0)
2101     sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
2102
2103   /* Nobody asks for DSCP, leave it as-is */
2104   if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
2105       !webrtc->priv->tos_attached)
2106     return;
2107
2108   /* If one stream has a non-default priority, then everyone else does too */
2109   gst_webrtc_bin_attach_tos (webrtc);
2110
2111   webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
2112       sctp_priority);
2113 }
2114
2115 static void
2116 gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
2117     GstWebRTCICETransport * transport)
2118 {
2119   GstPad *pad;
2120   GWeakRef *weak;
2121
2122   pad = gst_element_get_static_pad (transport->sink, "sink");
2123
2124   weak = g_new0 (GWeakRef, 1);
2125   g_weak_ref_init (weak, webrtc);
2126
2127   gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2128       _nicesink_pad_probe, weak, (GDestroyNotify) weak_free);
2129   gst_object_unref (pad);
2130 }
2131
2132 static void
2133 gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
2134 {
2135   guint i;
2136
2137   if (webrtc->priv->tos_attached)
2138     return;
2139   webrtc->priv->tos_attached = TRUE;
2140
2141   for (i = 0; i < webrtc->priv->transports->len; i++) {
2142     TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
2143
2144     gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
2145
2146     gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
2147         stream->transport->transport);
2148   }
2149
2150   gst_webrtc_bin_update_sctp_priority (webrtc);
2151 }
2152
2153 static WebRTCTransceiver *
2154 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
2155     GstWebRTCRTPTransceiverDirection direction, guint mline, GstWebRTCKind kind,
2156     GstCaps * codec_preferences)
2157 {
2158   WebRTCTransceiver *trans;
2159   GstWebRTCRTPTransceiver *rtp_trans;
2160   GstWebRTCRTPSender *sender;
2161   GstWebRTCRTPReceiver *receiver;
2162
2163   sender = gst_webrtc_rtp_sender_new ();
2164   receiver = gst_webrtc_rtp_receiver_new ();
2165   trans = webrtc_transceiver_new (webrtc, sender, receiver);
2166   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2167   rtp_trans->direction = direction;
2168   rtp_trans->mline = mline;
2169   rtp_trans->kind = kind;
2170   rtp_trans->codec_preferences =
2171       codec_preferences ? gst_caps_ref (codec_preferences) : NULL;
2172   /* FIXME: We don't support stopping transceiver yet so they're always not stopped */
2173   rtp_trans->stopped = FALSE;
2174
2175   g_signal_connect_object (sender, "notify::priority",
2176       G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
2177
2178   g_ptr_array_add (webrtc->priv->transceivers, trans);
2179
2180   gst_object_unref (sender);
2181   gst_object_unref (receiver);
2182
2183   g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
2184       0, trans);
2185
2186   return trans;
2187 }
2188
2189 static TransportStream *
2190 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2191 {
2192   GstWebRTCDTLSTransport *transport;
2193   TransportStream *ret;
2194   gchar *pad_name;
2195
2196   /* FIXME: how to parametrize the sender and the receiver */
2197   ret = transport_stream_new (webrtc, session_id);
2198   transport = ret->transport;
2199
2200   g_signal_connect (G_OBJECT (transport->transport), "notify::state",
2201       G_CALLBACK (_on_ice_transport_notify_state), webrtc);
2202   g_signal_connect (G_OBJECT (transport->transport),
2203       "notify::gathering-state",
2204       G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
2205   g_signal_connect (G_OBJECT (transport), "notify::state",
2206       G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
2207   if (webrtc->priv->tos_attached)
2208     gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
2209
2210   gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
2211   gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
2212   g_ptr_array_add (webrtc->priv->transports, ret);
2213
2214   pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
2215   if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
2216           GST_ELEMENT (webrtc->rtpbin), pad_name))
2217     g_warn_if_reached ();
2218   g_free (pad_name);
2219
2220   pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
2221   if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2222           GST_ELEMENT (ret->send_bin), "rtcp_sink"))
2223     g_warn_if_reached ();
2224   g_free (pad_name);
2225
2226   GST_TRACE_OBJECT (webrtc,
2227       "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
2228
2229   return ret;
2230 }
2231
2232 static TransportStream *
2233 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
2234 {
2235   TransportStream *ret;
2236
2237   ret = _find_transport_for_session (webrtc, session_id);
2238
2239   if (!ret)
2240     ret = _create_transport_channel (webrtc, session_id);
2241
2242   gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
2243   gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
2244
2245   return ret;
2246 }
2247
2248 /* this is called from the webrtc thread with the pc lock held */
2249 static void
2250 _on_data_channel_ready_state (WebRTCDataChannel * channel,
2251     GParamSpec * pspec, GstWebRTCBin * webrtc)
2252 {
2253   GstWebRTCDataChannelState ready_state;
2254
2255   g_object_get (channel, "ready-state", &ready_state, NULL);
2256
2257   if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
2258     gboolean found;
2259
2260     DC_LOCK (webrtc);
2261     found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel);
2262     if (found == FALSE) {
2263       GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
2264       DC_UNLOCK (webrtc);
2265       return;
2266     }
2267
2268     g_ptr_array_add (webrtc->priv->data_channels, gst_object_ref (channel));
2269     DC_UNLOCK (webrtc);
2270
2271     gst_webrtc_bin_update_sctp_priority (webrtc);
2272
2273     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
2274         channel);
2275   } else if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) {
2276     gboolean found;
2277
2278     DC_LOCK (webrtc);
2279     found = g_ptr_array_remove (webrtc->priv->pending_data_channels, channel)
2280         || g_ptr_array_remove (webrtc->priv->data_channels, channel);
2281
2282     if (found == FALSE) {
2283       GST_FIXME_OBJECT (webrtc, "Received close for unknown data channel");
2284     }
2285     DC_UNLOCK (webrtc);
2286   }
2287 }
2288
2289 static void
2290 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
2291     GstWebRTCBin * webrtc)
2292 {
2293   WebRTCDataChannel *channel;
2294   guint stream_id;
2295   GstPad *sink_pad;
2296
2297   if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
2298     return;
2299
2300   DC_LOCK (webrtc);
2301   channel = _find_data_channel_for_id (webrtc, stream_id);
2302   if (!channel) {
2303     channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL);
2304     channel->parent.id = stream_id;
2305     channel->webrtcbin = webrtc;
2306
2307     gst_bin_add (GST_BIN (webrtc), channel->appsrc);
2308     gst_bin_add (GST_BIN (webrtc), channel->appsink);
2309
2310     gst_element_sync_state_with_parent (channel->appsrc);
2311     gst_element_sync_state_with_parent (channel->appsink);
2312
2313     webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2314
2315     g_ptr_array_add (webrtc->priv->pending_data_channels, channel);
2316   }
2317   DC_UNLOCK (webrtc);
2318
2319   g_signal_connect (channel, "notify::ready-state",
2320       G_CALLBACK (_on_data_channel_ready_state), webrtc);
2321
2322   sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
2323   if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
2324     GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
2325         GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
2326   gst_object_unref (sink_pad);
2327 }
2328
2329 static void
2330 _on_sctp_state_notify (WebRTCSCTPTransport * sctp, GParamSpec * pspec,
2331     GstWebRTCBin * webrtc)
2332 {
2333   GstWebRTCSCTPTransportState state;
2334
2335   g_object_get (sctp, "state", &state, NULL);
2336
2337   if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
2338     int i;
2339
2340     GST_DEBUG_OBJECT (webrtc, "SCTP association established");
2341
2342     DC_LOCK (webrtc);
2343     for (i = 0; i < webrtc->priv->data_channels->len; i++) {
2344       WebRTCDataChannel *channel;
2345
2346       channel = g_ptr_array_index (webrtc->priv->data_channels, i);
2347
2348       webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
2349
2350       if (!channel->parent.negotiated && !channel->opened)
2351         webrtc_data_channel_start_negotiation (channel);
2352     }
2353     DC_UNLOCK (webrtc);
2354   }
2355 }
2356
2357 /* Forward declaration so we can easily disconnect the signal handler */
2358 static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2359     GParamSpec * pspec, GstWebRTCBin * webrtc);
2360
2361 static GstStructure *
2362 _sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused)
2363 {
2364   TransportStream *stream;
2365   GstWebRTCDTLSTransport *transport;
2366   GstWebRTCDTLSTransportState dtls_state;
2367   WebRTCSCTPTransport *sctp_transport;
2368
2369   stream = webrtc->priv->data_channel_transport;
2370   transport = stream->transport;
2371
2372   g_object_get (transport, "state", &dtls_state, NULL);
2373   /* Not connected yet so just return */
2374   if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2375     GST_DEBUG_OBJECT (webrtc,
2376         "Data channel DTLS connection is not ready yet: %d", dtls_state);
2377     return NULL;
2378   }
2379
2380   GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready");
2381   sctp_transport = webrtc->priv->sctp_transport;
2382
2383   /* Not locked state anymore so this was already taken care of before */
2384   if (!gst_element_is_locked_state (sctp_transport->sctpdec))
2385     return NULL;
2386
2387   /* Start up the SCTP elements now that the DTLS connection is established */
2388   gst_element_set_locked_state (sctp_transport->sctpdec, FALSE);
2389   gst_element_set_locked_state (sctp_transport->sctpenc, FALSE);
2390
2391   gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec));
2392   gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc));
2393
2394   if (sctp_transport->sctpdec_block_id) {
2395     GstPad *receive_srcpad;
2396
2397     receive_srcpad =
2398         gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2399         "data_src");
2400     gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id);
2401
2402     sctp_transport->sctpdec_block_id = 0;
2403     gst_object_unref (receive_srcpad);
2404   }
2405
2406   g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state,
2407       webrtc);
2408
2409   return NULL;
2410 }
2411
2412 static void
2413 _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport,
2414     GParamSpec * pspec, GstWebRTCBin * webrtc)
2415 {
2416   GstWebRTCDTLSTransportState dtls_state;
2417
2418   g_object_get (transport, "state", &dtls_state, NULL);
2419
2420   GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d",
2421       dtls_state);
2422
2423   /* Connected now, so schedule a task to update the state of the SCTP
2424    * elements */
2425   if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) {
2426     gst_webrtc_bin_enqueue_task (webrtc,
2427         (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2428   }
2429 }
2430
2431 static GstPadProbeReturn
2432 sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
2433 {
2434   /* Drop all events: we don't care about them and don't want to block on
2435    * them. Sticky events would be forwarded again later once we unblock
2436    * and we don't want to forward them here already because that might
2437    * cause a spurious GST_FLOW_FLUSHING */
2438   if (GST_IS_EVENT (info->data))
2439     return GST_PAD_PROBE_DROP;
2440
2441   /* But block on any actual data-flow so we don't accidentally send that
2442    * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything
2443    * to silently stop.
2444    */
2445   GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
2446
2447   return GST_PAD_PROBE_OK;
2448 }
2449
2450 static TransportStream *
2451 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
2452 {
2453   if (!webrtc->priv->data_channel_transport) {
2454     TransportStream *stream;
2455     WebRTCSCTPTransport *sctp_transport;
2456
2457     stream = _find_transport_for_session (webrtc, session_id);
2458
2459     if (!stream)
2460       stream = _create_transport_channel (webrtc, session_id);
2461
2462     webrtc->priv->data_channel_transport = stream;
2463
2464     if (!(sctp_transport = webrtc->priv->sctp_transport)) {
2465       sctp_transport = webrtc_sctp_transport_new ();
2466       sctp_transport->transport =
2467           g_object_ref (webrtc->priv->data_channel_transport->transport);
2468       sctp_transport->webrtcbin = webrtc;
2469
2470       /* Don't automatically start SCTP elements as part of webrtcbin. We
2471        * need to delay this until the DTLS transport is fully connected! */
2472       gst_element_set_locked_state (sctp_transport->sctpdec, TRUE);
2473       gst_element_set_locked_state (sctp_transport->sctpenc, TRUE);
2474
2475       gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
2476       gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
2477     }
2478
2479     g_signal_connect (sctp_transport->sctpdec, "pad-added",
2480         G_CALLBACK (_on_sctpdec_pad_added), webrtc);
2481     g_signal_connect (sctp_transport, "notify::state",
2482         G_CALLBACK (_on_sctp_state_notify), webrtc);
2483
2484     if (sctp_transport->sctpdec_block_id == 0) {
2485       GstPad *receive_srcpad;
2486       receive_srcpad =
2487           gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin),
2488           "data_src");
2489       sctp_transport->sctpdec_block_id =
2490           gst_pad_add_probe (receive_srcpad,
2491           GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
2492           (GstPadProbeCallback) sctp_pad_block, NULL, NULL);
2493       gst_object_unref (receive_srcpad);
2494     }
2495
2496     if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
2497             GST_ELEMENT (sctp_transport->sctpdec), "sink"))
2498       g_warn_if_reached ();
2499
2500     if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
2501             GST_ELEMENT (stream->send_bin), "data_sink"))
2502       g_warn_if_reached ();
2503
2504     gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
2505     gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2506
2507     if (!webrtc->priv->sctp_transport) {
2508       /* Connect to the notify::state signal to get notified when the DTLS
2509        * connection is established. Only then can we start the SCTP elements */
2510       g_signal_connect (stream->transport, "notify::state",
2511           G_CALLBACK (_on_sctp_notify_dtls_state), webrtc);
2512
2513       /* As this would be racy otherwise, also schedule a task that checks the
2514        * current state of the connection already without getting the signal
2515        * called */
2516       gst_webrtc_bin_enqueue_task (webrtc,
2517           (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL);
2518     }
2519
2520     webrtc->priv->sctp_transport = sctp_transport;
2521
2522     gst_webrtc_bin_update_sctp_priority (webrtc);
2523   }
2524
2525   return webrtc->priv->data_channel_transport;
2526 }
2527
2528 static TransportStream *
2529 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
2530     gboolean is_datachannel)
2531 {
2532   if (is_datachannel)
2533     return _get_or_create_data_channel_transports (webrtc, session_id);
2534   else
2535     return _get_or_create_rtp_transport_channel (webrtc, session_id);
2536 }
2537
2538 static guint
2539 g_array_find_uint (GArray * array, guint val)
2540 {
2541   guint i;
2542
2543   for (i = 0; i < array->len; i++) {
2544     if (g_array_index (array, guint, i) == val)
2545       return i;
2546   }
2547
2548   return G_MAXUINT;
2549 }
2550
2551 static gboolean
2552 _pick_available_pt (GArray * reserved_pts, guint * i)
2553 {
2554   gboolean ret = FALSE;
2555
2556   for (*i = 96; *i <= 127; (*i)++) {
2557     if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
2558       g_array_append_val (reserved_pts, *i);
2559       ret = TRUE;
2560       break;
2561     }
2562   }
2563
2564   return ret;
2565 }
2566
2567 static gboolean
2568 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2569     GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
2570     GstSDPMedia * media)
2571 {
2572   gboolean ret = TRUE;
2573
2574   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2575     goto done;
2576
2577   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
2578     guint pt;
2579     gchar *str;
2580
2581     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2582       goto done;
2583
2584     /* https://tools.ietf.org/html/rfc5109#section-14.1 */
2585
2586     str = g_strdup_printf ("%u", pt);
2587     gst_sdp_media_add_format (media, str);
2588     g_free (str);
2589     str = g_strdup_printf ("%u red/%d", pt, clockrate);
2590     gst_sdp_media_add_attribute (media, "rtpmap", str);
2591     g_free (str);
2592
2593     *rtx_target_pt = pt;
2594
2595     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2596       goto done;
2597
2598     str = g_strdup_printf ("%u", pt);
2599     gst_sdp_media_add_format (media, str);
2600     g_free (str);
2601     str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
2602     gst_sdp_media_add_attribute (media, "rtpmap", str);
2603     g_free (str);
2604   }
2605
2606 done:
2607   return ret;
2608 }
2609
2610 static gboolean
2611 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
2612     GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
2613     GstSDPMedia * media)
2614 {
2615   gboolean ret = TRUE;
2616
2617   if (trans->local_rtx_ssrc_map)
2618     gst_structure_free (trans->local_rtx_ssrc_map);
2619
2620   trans->local_rtx_ssrc_map =
2621       gst_structure_new_empty ("application/x-rtp-ssrc-map");
2622
2623   if (trans->do_nack) {
2624     guint pt;
2625     gchar *str;
2626
2627     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
2628       goto done;
2629
2630     /* https://tools.ietf.org/html/rfc4588#section-8.6 */
2631
2632     str = g_strdup_printf ("%u", target_ssrc);
2633     gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2634         g_random_int (), NULL);
2635     g_free (str);
2636
2637     str = g_strdup_printf ("%u", pt);
2638     gst_sdp_media_add_format (media, str);
2639     g_free (str);
2640
2641     str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
2642     gst_sdp_media_add_attribute (media, "rtpmap", str);
2643     g_free (str);
2644
2645     str = g_strdup_printf ("%u apt=%d", pt, target_pt);
2646     gst_sdp_media_add_attribute (media, "fmtp", str);
2647     g_free (str);
2648   }
2649
2650 done:
2651   return ret;
2652 }
2653
2654 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
2655 static gboolean
2656 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
2657     GstSDPMedia * media)
2658 {
2659   gchar *str;
2660
2661   str =
2662       g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
2663       g_value_get_uint (value));
2664   gst_sdp_media_add_attribute (media, "ssrc-group", str);
2665
2666   g_free (str);
2667
2668   return TRUE;
2669 }
2670
2671 typedef struct
2672 {
2673   GstSDPMedia *media;
2674   GstWebRTCBin *webrtc;
2675   WebRTCTransceiver *trans;
2676 } RtxSsrcData;
2677
2678 static gboolean
2679 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
2680 {
2681   gchar *str;
2682   GstStructure *sdes;
2683   const gchar *cname;
2684
2685   g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
2686   /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2687   cname = gst_structure_get_string (sdes, "cname");
2688
2689   /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2690   str =
2691       g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
2692       cname, GST_OBJECT_NAME (data->trans));
2693   gst_sdp_media_add_attribute (data->media, "ssrc", str);
2694   g_free (str);
2695
2696   str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
2697   gst_sdp_media_add_attribute (data->media, "ssrc", str);
2698   g_free (str);
2699
2700   gst_structure_free (sdes);
2701
2702   return TRUE;
2703 }
2704
2705 static void
2706 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
2707     WebRTCTransceiver * trans)
2708 {
2709   guint i;
2710   RtxSsrcData data = { media, webrtc, trans };
2711   const gchar *cname;
2712   GstStructure *sdes;
2713
2714   g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
2715   /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
2716   cname = gst_structure_get_string (sdes, "cname");
2717
2718   if (trans->local_rtx_ssrc_map)
2719     gst_structure_foreach (trans->local_rtx_ssrc_map,
2720         (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
2721
2722   for (i = 0; i < gst_caps_get_size (caps); i++) {
2723     const GstStructure *s = gst_caps_get_structure (caps, i);
2724     guint ssrc;
2725
2726     if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
2727       gchar *str;
2728
2729       /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
2730       str =
2731           g_strdup_printf ("%u msid:%s %s", ssrc, cname,
2732           GST_OBJECT_NAME (trans));
2733       gst_sdp_media_add_attribute (media, "ssrc", str);
2734       g_free (str);
2735
2736       str = g_strdup_printf ("%u cname:%s", ssrc, cname);
2737       gst_sdp_media_add_attribute (media, "ssrc", str);
2738       g_free (str);
2739     }
2740   }
2741
2742   gst_structure_free (sdes);
2743
2744   if (trans->local_rtx_ssrc_map)
2745     gst_structure_foreach (trans->local_rtx_ssrc_map,
2746         (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2747 }
2748
2749 static void
2750 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2751     GstSDPMedia * media)
2752 {
2753   gchar *cert, *fingerprint, *val;
2754
2755   g_object_get (transport, "certificate", &cert, NULL);
2756
2757   fingerprint =
2758       _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2759   g_free (cert);
2760   val =
2761       g_strdup_printf ("%s %s",
2762       _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2763   g_free (fingerprint);
2764
2765   gst_sdp_media_add_attribute (media, "fingerprint", val);
2766   g_free (val);
2767 }
2768
2769 static gchar *
2770 _parse_extmap (GQuark field_id, const GValue * value, GError ** error)
2771 {
2772   gchar *ret = NULL;
2773
2774   if (G_VALUE_HOLDS_STRING (value)) {
2775     ret = g_value_dup_string (value);
2776   } else if (G_VALUE_HOLDS (value, GST_TYPE_ARRAY)
2777       && gst_value_array_get_size (value) == 3) {
2778     const GValue *val;
2779     const gchar *direction, *extensionname, *extensionattributes;
2780
2781     val = gst_value_array_get_value (value, 0);
2782     direction = g_value_get_string (val);
2783
2784     val = gst_value_array_get_value (value, 1);
2785     extensionname = g_value_get_string (val);
2786
2787     val = gst_value_array_get_value (value, 2);
2788     extensionattributes = g_value_get_string (val);
2789
2790     if (!extensionname || *extensionname == '\0')
2791       goto done;
2792
2793     if (direction && *direction != '\0' && extensionattributes
2794         && *extensionattributes != '\0') {
2795       ret =
2796           g_strdup_printf ("/%s %s %s", direction, extensionname,
2797           extensionattributes);
2798     } else if (direction && *direction != '\0') {
2799       ret = g_strdup_printf ("/%s %s", direction, extensionname);
2800     } else if (extensionattributes && *extensionattributes != '\0') {
2801       ret = g_strdup_printf ("%s %s", extensionname, extensionattributes);
2802     } else {
2803       ret = g_strdup (extensionname);
2804     }
2805   }
2806
2807   if (!ret && error) {
2808     gchar *val_str = gst_value_serialize (value);
2809
2810     g_set_error (error, GST_WEBRTC_BIN_ERROR,
2811         GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
2812         "Invalid value for %s: %s", g_quark_to_string (field_id), val_str);
2813     g_free (val_str);
2814   }
2815
2816 done:
2817   return ret;
2818 }
2819
2820 typedef struct
2821 {
2822   gboolean ret;
2823   GstStructure *extmap;
2824   GError **error;
2825 } ExtmapData;
2826
2827 static gboolean
2828 _dedup_extmap_field (GQuark field_id, const GValue * value, ExtmapData * data)
2829 {
2830   gboolean is_extmap =
2831       g_str_has_prefix (g_quark_to_string (field_id), "extmap-");
2832
2833   if (!data->ret)
2834     goto done;
2835
2836   if (is_extmap) {
2837     gchar *new_value = _parse_extmap (field_id, value, data->error);
2838
2839     if (!new_value) {
2840       data->ret = FALSE;
2841       goto done;
2842     }
2843
2844     if (gst_structure_id_has_field (data->extmap, field_id)) {
2845       gchar *old_value =
2846           _parse_extmap (field_id, gst_structure_id_get_value (data->extmap,
2847               field_id), NULL);
2848
2849       g_assert (old_value);
2850
2851       if (g_strcmp0 (new_value, old_value)) {
2852         GST_ERROR
2853             ("extmap contains different values for id %s (%s != %s)",
2854             g_quark_to_string (field_id), old_value, new_value);
2855         g_set_error (data->error, GST_WEBRTC_BIN_ERROR,
2856             GST_WEBRTC_BIN_ERROR_CAPS_NEGOTIATION_FAILED,
2857             "extmap contains different values for id %s (%s != %s)",
2858             g_quark_to_string (field_id), old_value, new_value);
2859         data->ret = FALSE;
2860       }
2861
2862       g_free (old_value);
2863
2864     }
2865
2866     if (data->ret) {
2867       gst_structure_id_set_value (data->extmap, field_id, value);
2868     }
2869
2870     g_free (new_value);
2871   }
2872
2873 done:
2874   return !is_extmap;
2875 }
2876
2877 static GstStructure *
2878 _gather_extmap (GstCaps * caps, GError ** error)
2879 {
2880   ExtmapData edata =
2881       { TRUE, gst_structure_new_empty ("application/x-extmap"), error };
2882   guint i, n;
2883
2884   n = gst_caps_get_size (caps);
2885
2886   for (i = 0; i < n; i++) {
2887     GstStructure *s = gst_caps_get_structure (caps, i);
2888
2889     gst_structure_filter_and_map_in_place (s,
2890         (GstStructureFilterMapFunc) _dedup_extmap_field, &edata);
2891
2892     if (!edata.ret) {
2893       gst_clear_structure (&edata.extmap);
2894       break;
2895     }
2896   }
2897
2898   return edata.extmap;
2899 }
2900
2901 static gboolean
2902 _copy_field (GQuark field_id, const GValue * value, GstStructure * s)
2903 {
2904   gst_structure_id_set_value (s, field_id, value);
2905
2906   return TRUE;
2907 }
2908
2909 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2910 static gboolean
2911 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2912     GstWebRTCRTPTransceiver * trans, guint media_idx,
2913     GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2914     gchar * bundle_pwd, GArray * reserved_pts, GHashTable * all_mids,
2915     GError ** error)
2916 {
2917   /* TODO:
2918    * rtp header extensions
2919    * ice attributes
2920    * rtx
2921    * fec
2922    * msid-semantics
2923    * msid
2924    * dtls fingerprints
2925    * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2926    */
2927   GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2928   gchar *direction, *sdp_mid, *ufrag, *pwd;
2929   gboolean bundle_only;
2930   GstCaps *caps;
2931   GstStructure *extmap;
2932   int i;
2933
2934   if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2935       || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2936     return FALSE;
2937
2938   g_assert (trans->mline == -1 || trans->mline == media_idx);
2939
2940   bundle_only = bundled_mids && bundle_idx != media_idx
2941       && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2942
2943   /* mandated by JSEP */
2944   gst_sdp_media_add_attribute (media, "setup", "actpass");
2945
2946   /* FIXME: deal with ICE restarts */
2947   if (last_offer && trans->mline != -1 && trans->mid) {
2948     ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2949     pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2950     GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2951   } else {
2952     GST_DEBUG_OBJECT (trans,
2953         "%u Generating new ice parameters mline %i, mid %s", media_idx,
2954         trans->mline, trans->mid);
2955     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2956       _generate_ice_credentials (&ufrag, &pwd);
2957     } else {
2958       g_assert (bundle_ufrag && bundle_pwd);
2959       ufrag = g_strdup (bundle_ufrag);
2960       pwd = g_strdup (bundle_pwd);
2961     }
2962   }
2963
2964   gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2965   gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2966   g_free (ufrag);
2967   g_free (pwd);
2968
2969   gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2970   gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2971   gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2972
2973   if (bundle_only) {
2974     gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2975   }
2976
2977   /* FIXME: negotiate this */
2978   /* FIXME: when bundle_only, these should not be added:
2979    * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2980    * However, this causes incompatibilities with current versions
2981    * of the major browsers */
2982   gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2983   gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2984
2985   direction =
2986       _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2987       trans->direction);
2988   gst_sdp_media_add_attribute (media, direction, "");
2989   g_free (direction);
2990
2991   caps = _find_codec_preferences (webrtc, trans, media_idx, error);
2992
2993   if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2994     GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2995     if (caps)
2996       gst_caps_unref (caps);
2997     return FALSE;
2998   }
2999
3000   caps = gst_caps_make_writable (caps);
3001
3002   /* When an extmap is defined twice for the same ID, firefox complains and
3003    * errors out (chrome is smart enough to accept strict duplicates).
3004    *
3005    * To work around this, we deduplicate extmap attributes, and also error
3006    * out when a different extmap is defined for the same ID.
3007    *
3008    * _gather_extmap will strip out all extmap- fields, which will then be
3009    * added upon adding the first format for the media.
3010    */
3011   extmap = _gather_extmap (caps, error);
3012
3013   if (!extmap) {
3014     GST_ERROR_OBJECT (webrtc,
3015         "Failed to build extmap for transceiver %" GST_PTR_FORMAT, trans);
3016     gst_caps_unref (caps);
3017     return FALSE;
3018   }
3019
3020   caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
3021       caps);
3022
3023   for (i = 0; i < gst_caps_get_size (caps); i++) {
3024     GstCaps *format = gst_caps_new_empty ();
3025     GstStructure *s = gst_structure_copy (gst_caps_get_structure (caps, i));
3026
3027     if (i == 0) {
3028       gst_structure_foreach (extmap, (GstStructureForeachFunc) _copy_field, s);
3029     }
3030
3031     gst_caps_append_structure (format, s);
3032
3033     GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
3034         " to %u-th media", i, format, media_idx);
3035
3036     /* this only looks at the first structure so we loop over the given caps
3037      * and add each structure inside it piecemeal */
3038     gst_sdp_media_set_media_from_caps (format, media);
3039
3040     gst_caps_unref (format);
3041   }
3042
3043   gst_clear_structure (&extmap);
3044
3045   {
3046     const GstStructure *s = gst_caps_get_structure (caps, 0);
3047     gint clockrate = -1;
3048     gint rtx_target_pt;
3049     gint original_rtx_target_pt;        /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
3050     guint rtx_target_ssrc = -1;
3051
3052     if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
3053         webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3054       g_array_append_val (reserved_pts, rtx_target_pt);
3055
3056     original_rtx_target_pt = rtx_target_pt;
3057
3058     if (!gst_structure_get_int (s, "clock-rate", &clockrate))
3059       GST_WARNING_OBJECT (webrtc,
3060           "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
3061     if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
3062       GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
3063           caps);
3064
3065     _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3066         clockrate, &rtx_target_pt, media);
3067     _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3068         clockrate, rtx_target_pt, rtx_target_ssrc, media);
3069     if (original_rtx_target_pt != rtx_target_pt)
3070       _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
3071           clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
3072   }
3073
3074   _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
3075
3076   /* Some identifier; we also add the media name to it so it's identifiable */
3077   if (trans->mid) {
3078     gst_sdp_media_add_attribute (media, "mid", trans->mid);
3079   } else {
3080     /* Make sure to avoid mid collisions */
3081     while (TRUE) {
3082       sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3083           webrtc->priv->media_counter++);
3084       if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3085         g_free (sdp_mid);
3086       } else {
3087         gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3088         g_hash_table_insert (all_mids, sdp_mid, NULL);
3089         break;
3090       }
3091     }
3092   }
3093
3094   /* TODO:
3095    * - add a=candidate lines for gathered candidates
3096    */
3097
3098   if (trans->sender) {
3099     if (!trans->sender->transport) {
3100       TransportStream *item;
3101
3102       item =
3103           _get_or_create_transport_stream (webrtc,
3104           bundled_mids ? bundle_idx : media_idx, FALSE);
3105
3106       webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
3107     }
3108
3109     _add_fingerprint_to_media (trans->sender->transport, media);
3110   }
3111
3112   if (bundled_mids) {
3113     const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3114
3115     g_assert (mid);
3116     g_string_append_printf (bundled_mids, " %s", mid);
3117   }
3118
3119   gst_caps_unref (caps);
3120
3121   return TRUE;
3122 }
3123
3124 static void
3125 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
3126 {
3127   if (pad->received_caps) {
3128     GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
3129     gint pt;
3130
3131     if (gst_structure_get_int (s, "payload", &pt)) {
3132       GST_TRACE_OBJECT (pad, "have reserved pt %u from received caps", pt);
3133       g_array_append_val (reserved_pts, pt);
3134     }
3135   }
3136 }
3137
3138 static GArray *
3139 gather_reserved_pts (GstWebRTCBin * webrtc)
3140 {
3141   GstElement *element = GST_ELEMENT (webrtc);
3142   GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3143   guint i;
3144
3145   GST_OBJECT_LOCK (webrtc);
3146   g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
3147   g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
3148       reserved_pts);
3149
3150   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3151     GstWebRTCRTPTransceiver *trans;
3152
3153     trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3154     GST_OBJECT_LOCK (trans);
3155     if (trans->codec_preferences) {
3156       guint j, n;
3157       gint pt;
3158
3159       n = gst_caps_get_size (trans->codec_preferences);
3160       for (j = 0; j < n; j++) {
3161         GstStructure *s = gst_caps_get_structure (trans->codec_preferences, j);
3162         if (gst_structure_get_int (s, "payload", &pt)) {
3163           GST_TRACE_OBJECT (trans, "have reserved pt %u from codec preferences",
3164               pt);
3165           g_array_append_val (reserved_pts, pt);
3166         }
3167       }
3168     }
3169     GST_OBJECT_UNLOCK (trans);
3170   }
3171   GST_OBJECT_UNLOCK (webrtc);
3172
3173   return reserved_pts;
3174 }
3175
3176 static gboolean
3177 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
3178     GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
3179     gchar * bundle_ufrag, gchar * bundle_pwd, GHashTable * all_mids)
3180 {
3181   GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3182   gchar *ufrag, *pwd, *sdp_mid;
3183   gboolean bundle_only = bundled_mids
3184       && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
3185       && gst_sdp_message_medias_len (msg) != bundle_idx;
3186   guint last_data_index = G_MAXUINT;
3187
3188   /* add data channel support */
3189   if (webrtc->priv->data_channels->len == 0)
3190     return FALSE;
3191
3192   if (last_offer) {
3193     last_data_index = _message_get_datachannel_index (last_offer);
3194     if (last_data_index < G_MAXUINT) {
3195       g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
3196       /* XXX: is this always true when recycling transceivers?
3197        * i.e. do we always put the data channel in the same mline */
3198       g_assert (last_data_index == gst_sdp_message_medias_len (msg));
3199     }
3200   }
3201
3202   /* mandated by JSEP */
3203   gst_sdp_media_add_attribute (media, "setup", "actpass");
3204
3205   /* FIXME: only needed when restarting ICE */
3206   if (last_offer && last_data_index < G_MAXUINT) {
3207     ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
3208     pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
3209   } else {
3210     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3211       _generate_ice_credentials (&ufrag, &pwd);
3212     } else {
3213       ufrag = g_strdup (bundle_ufrag);
3214       pwd = g_strdup (bundle_pwd);
3215     }
3216   }
3217   gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3218   gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3219   g_free (ufrag);
3220   g_free (pwd);
3221
3222   gst_sdp_media_set_media (media, "application");
3223   gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
3224   gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3225   gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3226   gst_sdp_media_add_format (media, "webrtc-datachannel");
3227
3228   if (bundle_idx != gst_sdp_message_medias_len (msg))
3229     gst_sdp_media_add_attribute (media, "bundle-only", NULL);
3230
3231   if (last_offer && last_data_index < G_MAXUINT) {
3232     const GstSDPMedia *last_data_media;
3233     const gchar *mid;
3234
3235     last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
3236     mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
3237
3238     gst_sdp_media_add_attribute (media, "mid", mid);
3239   } else {
3240     /* Make sure to avoid mid collisions */
3241     while (TRUE) {
3242       sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
3243           webrtc->priv->media_counter++);
3244       if (g_hash_table_contains (all_mids, (gpointer) sdp_mid)) {
3245         g_free (sdp_mid);
3246       } else {
3247         gst_sdp_media_add_attribute (media, "mid", sdp_mid);
3248         g_hash_table_insert (all_mids, sdp_mid, NULL);
3249         break;
3250       }
3251     }
3252   }
3253
3254   if (bundled_mids) {
3255     const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3256
3257     g_assert (mid);
3258     g_string_append_printf (bundled_mids, " %s", mid);
3259   }
3260
3261   /* FIXME: negotiate this properly */
3262   gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3263
3264   _get_or_create_data_channel_transports (webrtc,
3265       bundled_mids ? 0 : webrtc->priv->transceivers->len);
3266   _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
3267
3268   return TRUE;
3269 }
3270
3271 /* TODO: use the options argument */
3272 static GstSDPMessage *
3273 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3274     GError ** error)
3275 {
3276   GstSDPMessage *ret = NULL;
3277   GString *bundled_mids = NULL;
3278   gchar *bundle_ufrag = NULL;
3279   gchar *bundle_pwd = NULL;
3280   GArray *reserved_pts = NULL;
3281   GHashTable *all_mids =
3282       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
3283
3284   GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
3285   GList *seen_transceivers = NULL;
3286   guint media_idx = 0;
3287   int i;
3288
3289   gst_sdp_message_new (&ret);
3290
3291   gst_sdp_message_set_version (ret, "0");
3292   {
3293     gchar *v, *sess_id;
3294     v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
3295     if (last_offer) {
3296       const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
3297       sess_id = g_strdup (origin->sess_id);
3298     } else {
3299       sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
3300     }
3301     gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
3302     g_free (sess_id);
3303     g_free (v);
3304   }
3305   gst_sdp_message_set_session_name (ret, "-");
3306   gst_sdp_message_add_time (ret, "0", "0", NULL);
3307   gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
3308
3309   if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
3310     bundled_mids = g_string_new ("BUNDLE");
3311   } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
3312     bundled_mids = g_string_new ("BUNDLE");
3313   }
3314
3315   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3316     GStrv last_bundle = NULL;
3317     guint bundle_media_index;
3318
3319     reserved_pts = gather_reserved_pts (webrtc);
3320     if (last_offer && _parse_bundle (last_offer, &last_bundle, NULL)
3321         && last_bundle && last_bundle && last_bundle[0]
3322         && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
3323       bundle_ufrag =
3324           g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
3325       bundle_pwd =
3326           g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
3327     } else {
3328       _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3329     }
3330
3331     g_strfreev (last_bundle);
3332   }
3333
3334   /* FIXME: recycle transceivers */
3335
3336   /* Fill up the renegotiated streams first */
3337   if (last_offer) {
3338     for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
3339       GstWebRTCRTPTransceiver *trans = NULL;
3340       const GstSDPMedia *last_media;
3341
3342       last_media = gst_sdp_message_get_media (last_offer, i);
3343
3344       if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
3345           || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
3346         const gchar *last_mid;
3347         int j;
3348         last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
3349
3350         for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3351           trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3352
3353           if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
3354             GstSDPMedia *media;
3355             const gchar *mid;
3356             WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
3357
3358             g_assert (!g_list_find (seen_transceivers, trans));
3359
3360             if (wtrans->mline_locked && trans->mline != media_idx) {
3361               g_set_error (error, GST_WEBRTC_BIN_ERROR,
3362                   GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3363                   "Previous negotiatied transceiver %"
3364                   GST_PTR_FORMAT " with mid %s was in mline %d but transceiver"
3365                   " has locked mline %u", trans, trans->mid, media_idx,
3366                   trans->mline);
3367               goto cancel_offer;
3368             }
3369
3370             GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
3371                 GST_PTR_FORMAT " with mid %s into media index %u", trans,
3372                 trans->mid, media_idx);
3373
3374             /* FIXME: deal with format changes */
3375             gst_sdp_media_copy (last_media, &media);
3376             _media_replace_direction (media, trans->direction);
3377
3378             mid = gst_sdp_media_get_attribute_val (media, "mid");
3379             g_assert (mid);
3380
3381             if (g_hash_table_contains (all_mids, mid)) {
3382               gst_sdp_media_free (media);
3383               g_set_error (error, GST_WEBRTC_BIN_ERROR,
3384                   GST_WEBRTC_BIN_ERROR_FAILED,
3385                   "Duplicate mid %s when creating offer", mid);
3386               goto cancel_offer;
3387             }
3388
3389             g_hash_table_insert (all_mids, g_strdup (mid), NULL);
3390
3391             if (bundled_mids)
3392               g_string_append_printf (bundled_mids, " %s", mid);
3393
3394             gst_sdp_message_add_media (ret, media);
3395             media_idx++;
3396
3397             gst_sdp_media_free (media);
3398             seen_transceivers = g_list_prepend (seen_transceivers, trans);
3399             break;
3400           }
3401         }
3402       } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
3403               "application") == 0) {
3404         GstSDPMedia media = { 0, };
3405         gst_sdp_media_init (&media);
3406         if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3407                 bundle_ufrag, bundle_pwd, all_mids)) {
3408           gst_sdp_message_add_media (ret, &media);
3409           media_idx++;
3410         } else {
3411           gst_sdp_media_uninit (&media);
3412         }
3413       }
3414     }
3415   }
3416
3417   /* First, go over all transceivers and gather existing mids */
3418   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3419     GstWebRTCRTPTransceiver *trans;
3420
3421     trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3422
3423     if (g_list_find (seen_transceivers, trans))
3424       continue;
3425
3426     if (trans->mid) {
3427       if (g_hash_table_contains (all_mids, trans->mid)) {
3428         g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_FAILED,
3429             "Duplicate mid %s when creating offer", trans->mid);
3430         goto cancel_offer;
3431       }
3432
3433       g_hash_table_insert (all_mids, g_strdup (trans->mid), NULL);
3434     }
3435   }
3436
3437
3438   /* add any extra streams */
3439   for (;;) {
3440     GstWebRTCRTPTransceiver *trans = NULL;
3441     GstSDPMedia media = { 0, };
3442
3443     /* First find a transceiver requesting this m-line */
3444     trans = _find_transceiver_for_mline (webrtc, media_idx);
3445
3446     if (trans) {
3447       /* We can't have seen it already, because it is locked to this line */
3448       g_assert (!g_list_find (seen_transceivers, trans));
3449       seen_transceivers = g_list_prepend (seen_transceivers, trans);
3450     } else {
3451       /* Otherwise find a free transceiver */
3452       for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3453         WebRTCTransceiver *wtrans;
3454
3455         trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3456         wtrans = WEBRTC_TRANSCEIVER (trans);
3457
3458         /* don't add transceivers twice */
3459         if (g_list_find (seen_transceivers, trans))
3460           continue;
3461
3462         /* Ignore transceivers with a locked mline, as they would have been
3463          * found above or will be used later */
3464         if (wtrans->mline_locked)
3465           continue;
3466
3467         seen_transceivers = g_list_prepend (seen_transceivers, trans);
3468         /* don't add stopped transceivers */
3469         if (trans->stopped) {
3470           continue;
3471         }
3472
3473         /* Otherwise take it */
3474         break;
3475       }
3476
3477       /* Stop if we got all transceivers */
3478       if (i == webrtc->priv->transceivers->len) {
3479
3480         /* But try to add a data channel first, we do it here, because
3481          * it can allow a locked m-line to be put after, so we need to
3482          * do another iteration after.
3483          */
3484         if (_message_get_datachannel_index (ret) == G_MAXUINT) {
3485           GstSDPMedia media = { 0, };
3486           gst_sdp_media_init (&media);
3487           if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
3488                   bundle_ufrag, bundle_pwd, all_mids)) {
3489             gst_sdp_message_add_media (ret, &media);
3490             media_idx++;
3491             continue;
3492           } else {
3493             gst_sdp_media_uninit (&media);
3494           }
3495         }
3496
3497         /* Verify that we didn't ignore any locked m-line transceivers */
3498         for (i = 0; i < webrtc->priv->transceivers->len; i++) {
3499           WebRTCTransceiver *wtrans;
3500
3501           trans = g_ptr_array_index (webrtc->priv->transceivers, i);
3502           wtrans = WEBRTC_TRANSCEIVER (trans);
3503           /* don't add transceivers twice */
3504           if (g_list_find (seen_transceivers, trans))
3505             continue;
3506           g_assert (wtrans->mline_locked);
3507
3508           g_set_error (error, GST_WEBRTC_BIN_ERROR,
3509               GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
3510               "Tranceiver %" GST_PTR_FORMAT " with mid %s has locked mline %d"
3511               " but the whole offer only has %u sections", trans, trans->mid,
3512               trans->mline, media_idx);
3513           goto cancel_offer;
3514         }
3515         break;
3516       }
3517     }
3518
3519     gst_sdp_media_init (&media);
3520
3521     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3522       reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
3523     }
3524
3525     GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
3526         "index %u", trans, media_idx);
3527
3528     if (sdp_media_from_transceiver (webrtc, &media, trans, media_idx,
3529             bundled_mids, 0, bundle_ufrag, bundle_pwd, reserved_pts, all_mids,
3530             error)) {
3531       /* as per JSEP, a=rtcp-mux-only is only added for new streams */
3532       gst_sdp_media_add_attribute (&media, "rtcp-mux-only", "");
3533       gst_sdp_message_add_media (ret, &media);
3534       media_idx++;
3535     } else {
3536       gst_sdp_media_uninit (&media);
3537     }
3538
3539     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
3540       g_array_free (reserved_pts, TRUE);
3541       reserved_pts = NULL;
3542     }
3543     if (*error)
3544       goto cancel_offer;
3545   }
3546
3547   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3548     g_array_free (reserved_pts, TRUE);
3549     reserved_pts = NULL;
3550   }
3551
3552   webrtc->priv->max_sink_pad_serial = MAX (webrtc->priv->max_sink_pad_serial,
3553       media_idx);
3554
3555   g_assert (media_idx == gst_sdp_message_medias_len (ret));
3556
3557   if (bundled_mids) {
3558     gchar *mids = g_string_free (bundled_mids, FALSE);
3559
3560     gst_sdp_message_add_attribute (ret, "group", mids);
3561     g_free (mids);
3562     bundled_mids = NULL;
3563   }
3564
3565   /* FIXME: pre-emptively setup receiving elements when needed */
3566
3567   if (webrtc->priv->last_generated_answer)
3568     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3569   webrtc->priv->last_generated_answer = NULL;
3570   if (webrtc->priv->last_generated_offer)
3571     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3572   {
3573     GstSDPMessage *copy;
3574     gst_sdp_message_copy (ret, &copy);
3575     webrtc->priv->last_generated_offer =
3576         gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
3577   }
3578
3579 out:
3580   if (reserved_pts)
3581     g_array_free (reserved_pts, TRUE);
3582
3583   g_hash_table_unref (all_mids);
3584
3585   g_list_free (seen_transceivers);
3586
3587   if (bundle_ufrag)
3588     g_free (bundle_ufrag);
3589
3590   if (bundle_pwd)
3591     g_free (bundle_pwd);
3592
3593   if (bundled_mids)
3594     g_string_free (bundled_mids, TRUE);
3595
3596   return ret;
3597
3598 cancel_offer:
3599   gst_sdp_message_free (ret);
3600   ret = NULL;
3601   goto out;
3602 }
3603
3604 static void
3605 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
3606     gint * rtx_target_pt)
3607 {
3608   guint i;
3609
3610   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
3611     return;
3612
3613   for (i = 0; i < gst_caps_get_size (caps); i++) {
3614     const GstStructure *s = gst_caps_get_structure (caps, i);
3615
3616     if (gst_structure_has_name (s, "application/x-rtp")) {
3617       const gchar *encoding_name =
3618           gst_structure_get_string (s, "encoding-name");
3619       gint clock_rate;
3620       gint pt;
3621
3622       if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3623           gst_structure_get_int (s, "payload", &pt)) {
3624         if (!g_strcmp0 (encoding_name, "RED")) {
3625           gchar *str;
3626
3627           str = g_strdup_printf ("%u", pt);
3628           gst_sdp_media_add_format (media, str);
3629           g_free (str);
3630           str = g_strdup_printf ("%u red/%d", pt, clock_rate);
3631           *rtx_target_pt = pt;
3632           gst_sdp_media_add_attribute (media, "rtpmap", str);
3633           g_free (str);
3634         } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
3635           gchar *str;
3636
3637           str = g_strdup_printf ("%u", pt);
3638           gst_sdp_media_add_format (media, str);
3639           g_free (str);
3640           str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
3641           gst_sdp_media_add_attribute (media, "rtpmap", str);
3642           g_free (str);
3643         }
3644       }
3645     }
3646   }
3647 }
3648
3649 static void
3650 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
3651     GstCaps * offer_caps, gint target_pt, guint target_ssrc)
3652 {
3653   guint i;
3654   const GstStructure *s;
3655
3656   if (trans->local_rtx_ssrc_map)
3657     gst_structure_free (trans->local_rtx_ssrc_map);
3658
3659   trans->local_rtx_ssrc_map =
3660       gst_structure_new_empty ("application/x-rtp-ssrc-map");
3661
3662   for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
3663     s = gst_caps_get_structure (offer_caps, i);
3664
3665     if (gst_structure_has_name (s, "application/x-rtp")) {
3666       const gchar *encoding_name =
3667           gst_structure_get_string (s, "encoding-name");
3668       const gchar *apt_str = gst_structure_get_string (s, "apt");
3669       gint apt;
3670       gint clock_rate;
3671       gint pt;
3672
3673       if (!apt_str)
3674         continue;
3675
3676       apt = atoi (apt_str);
3677
3678       if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
3679           gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
3680         if (!g_strcmp0 (encoding_name, "RTX")) {
3681           gchar *str;
3682
3683           str = g_strdup_printf ("%u", pt);
3684           gst_sdp_media_add_format (media, str);
3685           g_free (str);
3686           str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
3687           gst_sdp_media_add_attribute (media, "rtpmap", str);
3688           g_free (str);
3689
3690           str = g_strdup_printf ("%d apt=%d", pt, apt);
3691           gst_sdp_media_add_attribute (media, "fmtp", str);
3692           g_free (str);
3693
3694           str = g_strdup_printf ("%u", target_ssrc);
3695           gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
3696               g_random_int (), NULL);
3697         }
3698       }
3699     }
3700   }
3701 }
3702
3703 static gboolean
3704 _update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
3705     const GstCaps * caps)
3706 {
3707   GstWebRTCKind kind = webrtc_kind_from_caps (caps);
3708
3709   if (trans->kind == kind)
3710     return TRUE;
3711
3712   if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
3713     trans->kind = kind;
3714     return TRUE;
3715   } else {
3716     return FALSE;
3717   }
3718 }
3719
3720 static void
3721 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
3722     guint * target_ssrc)
3723 {
3724   const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
3725
3726   gst_structure_get_int (s, "payload", target_pt);
3727   gst_structure_get_uint (s, "ssrc", target_ssrc);
3728 }
3729
3730 /* TODO: use the options argument */
3731 static GstSDPMessage *
3732 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
3733     GError ** error)
3734 {
3735   GstSDPMessage *ret = NULL;
3736   const GstWebRTCSessionDescription *pending_remote =
3737       webrtc->pending_remote_description;
3738   guint i;
3739   GStrv bundled = NULL;
3740   guint bundle_idx = 0;
3741   GString *bundled_mids = NULL;
3742   gchar *bundle_ufrag = NULL;
3743   gchar *bundle_pwd = NULL;
3744   GList *seen_transceivers = NULL;
3745   GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
3746
3747   if (!webrtc->pending_remote_description) {
3748     g_set_error_literal (error, GST_WEBRTC_BIN_ERROR,
3749         GST_WEBRTC_BIN_ERROR_INVALID_STATE,
3750         "Asked to create an answer without a remote description");
3751     return NULL;
3752   }
3753
3754   if (!_parse_bundle (pending_remote->sdp, &bundled, error))
3755     goto out;
3756
3757   if (bundled) {
3758     GStrv last_bundle = NULL;
3759     guint bundle_media_index;
3760
3761     if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
3762       g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
3763           "Bundle tag is %s but no media found matching", bundled[0]);
3764       goto out;
3765     }
3766
3767     if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
3768       bundled_mids = g_string_new ("BUNDLE");
3769     }
3770
3771     if (last_answer && _parse_bundle (last_answer, &last_bundle, NULL)
3772         && last_bundle && last_bundle[0]
3773         && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
3774       bundle_ufrag =
3775           g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
3776       bundle_pwd =
3777           g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
3778     } else {
3779       _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
3780     }
3781
3782     g_strfreev (last_bundle);
3783   }
3784
3785   gst_sdp_message_new (&ret);
3786
3787   gst_sdp_message_set_version (ret, "0");
3788   {
3789     const GstSDPOrigin *offer_origin =
3790         gst_sdp_message_get_origin (pending_remote->sdp);
3791     gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
3792         offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
3793   }
3794   gst_sdp_message_set_session_name (ret, "-");
3795
3796   for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
3797     const GstSDPAttribute *attr =
3798         gst_sdp_message_get_attribute (pending_remote->sdp, i);
3799
3800     if (g_strcmp0 (attr->key, "ice-options") == 0) {
3801       gst_sdp_message_add_attribute (ret, attr->key, attr->value);
3802     }
3803   }
3804
3805   for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
3806     GstSDPMedia *media = NULL;
3807     GstSDPMedia *offer_media;
3808     GstWebRTCDTLSSetup offer_setup, answer_setup;
3809     guint j, k;
3810     gboolean bundle_only;
3811     const gchar *mid;
3812
3813     offer_media =
3814         (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
3815     bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
3816
3817     gst_sdp_media_new (&media);
3818     if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
3819       gst_sdp_media_set_port_info (media, 0, 0);
3820     else
3821       gst_sdp_media_set_port_info (media, 9, 0);
3822     gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
3823
3824     {
3825       gchar *ufrag, *pwd;
3826
3827       /* FIXME: deal with ICE restarts */
3828       if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
3829         ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
3830         pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
3831       } else {
3832         if (!bundled) {
3833           _generate_ice_credentials (&ufrag, &pwd);
3834         } else {
3835           ufrag = g_strdup (bundle_ufrag);
3836           pwd = g_strdup (bundle_pwd);
3837         }
3838       }
3839       gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
3840       gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
3841       g_free (ufrag);
3842       g_free (pwd);
3843     }
3844
3845     for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
3846       const GstSDPAttribute *attr =
3847           gst_sdp_media_get_attribute (offer_media, j);
3848
3849       if (g_strcmp0 (attr->key, "mid") == 0
3850           || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
3851         gst_sdp_media_add_attribute (media, attr->key, attr->value);
3852         /* FIXME: handle anything we want to keep */
3853       }
3854     }
3855
3856     mid = gst_sdp_media_get_attribute_val (media, "mid");
3857     /* XXX: not strictly required but a lot of functionality requires a mid */
3858     g_assert (mid);
3859
3860     /* set the a=setup: attribute */
3861     offer_setup = _get_dtls_setup_from_media (offer_media);
3862     answer_setup = _intersect_dtls_setup (offer_setup);
3863     if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
3864       GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
3865           "transceiver direction");
3866       goto rejected;
3867     }
3868     _media_replace_setup (media, answer_setup);
3869
3870     if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
3871       int sctp_port;
3872
3873       if (gst_sdp_media_formats_len (offer_media) != 1) {
3874         GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
3875             "for webrtc-datachannel");
3876         goto rejected;
3877       }
3878       sctp_port = _get_sctp_port_from_media (offer_media);
3879       if (sctp_port == -1) {
3880         GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
3881         goto rejected;
3882       }
3883
3884       /* XXX: older browsers will produce a different SDP format for data
3885        * channel that is currently not parsed correctly */
3886       gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
3887
3888       gst_sdp_media_set_media (media, "application");
3889       gst_sdp_media_set_port_info (media, 9, 0);
3890       gst_sdp_media_add_format (media, "webrtc-datachannel");
3891
3892       /* FIXME: negotiate this properly on renegotiation */
3893       gst_sdp_media_add_attribute (media, "sctp-port", "5000");
3894
3895       _get_or_create_data_channel_transports (webrtc,
3896           bundled_mids ? bundle_idx : i);
3897
3898       if (bundled_mids) {
3899         g_assert (mid);
3900         g_string_append_printf (bundled_mids, " %s", mid);
3901       }
3902
3903       _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
3904           media);
3905     } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
3906         || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
3907       GstCaps *offer_caps, *answer_caps = NULL;
3908       GstWebRTCRTPTransceiver *rtp_trans = NULL;
3909       WebRTCTransceiver *trans = NULL;
3910       GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
3911       gint target_pt = -1;
3912       gint original_target_pt = -1;
3913       guint target_ssrc = 0;
3914
3915       gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
3916       offer_caps = _rtp_caps_from_media (offer_media);
3917
3918       if (last_answer && i < gst_sdp_message_medias_len (last_answer)
3919           && (rtp_trans =
3920               _find_transceiver (webrtc, mid,
3921                   (FindTransceiverFunc) match_for_mid))) {
3922         const GstSDPMedia *last_media =
3923             gst_sdp_message_get_media (last_answer, i);
3924         const gchar *last_mid =
3925             gst_sdp_media_get_attribute_val (last_media, "mid");
3926         GstCaps *current_caps;
3927
3928         /* FIXME: assumes no shenanigans with recycling transceivers */
3929         g_assert (g_strcmp0 (mid, last_mid) == 0);
3930
3931         current_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
3932         if (*error) {
3933           gst_caps_unref (offer_caps);
3934           goto rejected;
3935         }
3936         if (!current_caps)
3937           current_caps = _rtp_caps_from_media (last_media);
3938
3939         if (current_caps) {
3940           answer_caps = gst_caps_intersect (offer_caps, current_caps);
3941           if (gst_caps_is_empty (answer_caps)) {
3942             GST_WARNING_OBJECT (webrtc, "Caps from offer for m-line %d (%"
3943                 GST_PTR_FORMAT ") don't intersect with caps from codec"
3944                 " preferences and transceiver %" GST_PTR_FORMAT, i, offer_caps,
3945                 current_caps);
3946             gst_caps_unref (current_caps);
3947             gst_caps_unref (answer_caps);
3948             gst_caps_unref (offer_caps);
3949             goto rejected;
3950           }
3951           gst_caps_unref (current_caps);
3952         }
3953
3954         /* XXX: In theory we're meant to use the sendrecv formats for the
3955          * inactive direction however we don't know what that may be and would
3956          * require asking outside what it expects to possibly send later */
3957
3958         GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
3959             "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
3960             "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
3961       } else {
3962         for (j = 0; j < webrtc->priv->transceivers->len; j++) {
3963           GstCaps *trans_caps;
3964
3965           rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j);
3966
3967           if (g_list_find (seen_transceivers, rtp_trans)) {
3968             /* Don't double allocate a transceiver to multiple mlines */
3969             rtp_trans = NULL;
3970             continue;
3971           }
3972
3973           trans_caps = _find_codec_preferences (webrtc, rtp_trans, j, error);
3974           if (*error) {
3975             gst_caps_unref (offer_caps);
3976             goto rejected;
3977           }
3978
3979           GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
3980               " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
3981
3982           /* FIXME: technically this is a little overreaching as some fields we
3983            * we can deal with not having and/or we may have unrecognized fields
3984            * that we cannot actually support */
3985           if (trans_caps) {
3986             answer_caps = gst_caps_intersect (offer_caps, trans_caps);
3987             gst_caps_unref (trans_caps);
3988             if (answer_caps) {
3989               if (!gst_caps_is_empty (answer_caps)) {
3990                 GST_LOG_OBJECT (webrtc,
3991                     "found compatible transceiver %" GST_PTR_FORMAT
3992                     " for offer media %u", rtp_trans, i);
3993                 break;
3994               }
3995               gst_caps_unref (answer_caps);
3996               answer_caps = NULL;
3997             }
3998           }
3999           rtp_trans = NULL;
4000         }
4001       }
4002
4003       if (rtp_trans) {
4004         answer_dir = rtp_trans->direction;
4005         g_assert (answer_caps != NULL);
4006       } else {
4007         /* if no transceiver, then we only receive that stream and respond with
4008          * the intersection with the transceivers codec preferences caps */
4009         answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
4010       }
4011
4012       if (!rtp_trans) {
4013         GstCaps *trans_caps;
4014         GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
4015
4016         if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0)
4017           kind = GST_WEBRTC_KIND_AUDIO;
4018         else if (g_strcmp0 (gst_sdp_media_get_media (offer_media),
4019                 "video") == 0)
4020           kind = GST_WEBRTC_KIND_VIDEO;
4021         else
4022           GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
4023               GST_STR_NULL (gst_sdp_media_get_media (offer_media)));
4024
4025         trans = _create_webrtc_transceiver (webrtc, answer_dir, i, kind, NULL);
4026         rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4027
4028         GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
4029             " for mline %u with media kind %d", trans, i, kind);
4030
4031         trans_caps = _find_codec_preferences (webrtc, rtp_trans, i, error);
4032         if (*error) {
4033           gst_caps_unref (offer_caps);
4034           goto rejected;
4035         }
4036
4037         GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
4038             " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
4039
4040         /* FIXME: technically this is a little overreaching as some fields we
4041          * we can deal with not having and/or we may have unrecognized fields
4042          * that we cannot actually support */
4043         if (trans_caps) {
4044           answer_caps = gst_caps_intersect (offer_caps, trans_caps);
4045           gst_caps_unref (trans_caps);
4046         } else {
4047           answer_caps = gst_caps_ref (offer_caps);
4048         }
4049       } else {
4050         trans = WEBRTC_TRANSCEIVER (rtp_trans);
4051       }
4052
4053       seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
4054
4055       if (gst_caps_is_empty (answer_caps)) {
4056         GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
4057         gst_caps_unref (answer_caps);
4058         gst_caps_unref (offer_caps);
4059         goto rejected;
4060       }
4061
4062       if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps))
4063         GST_WARNING_OBJECT (webrtc,
4064             "Trying to change transceiver %d kind from %d to %d",
4065             rtp_trans->mline, rtp_trans->kind,
4066             webrtc_kind_from_caps (answer_caps));
4067
4068       if (!trans->do_nack) {
4069         answer_caps = gst_caps_make_writable (answer_caps);
4070         for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
4071           GstStructure *s = gst_caps_get_structure (answer_caps, k);
4072           gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
4073         }
4074       }
4075
4076       gst_sdp_media_set_media_from_caps (answer_caps, media);
4077
4078       _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
4079           &target_ssrc);
4080
4081       original_target_pt = target_pt;
4082
4083       _media_add_fec (media, trans, offer_caps, &target_pt);
4084       if (trans->do_nack) {
4085         _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
4086         if (target_pt != original_target_pt)
4087           _media_add_rtx (media, trans, offer_caps, original_target_pt,
4088               target_ssrc);
4089       }
4090
4091       if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
4092         _media_add_ssrcs (media, answer_caps, webrtc,
4093             WEBRTC_TRANSCEIVER (rtp_trans));
4094
4095       gst_caps_unref (answer_caps);
4096       answer_caps = NULL;
4097
4098       /* set the new media direction */
4099       offer_dir = _get_direction_from_media (offer_media);
4100       answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
4101       if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4102         GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
4103             "transceiver direction");
4104         gst_caps_unref (offer_caps);
4105         goto rejected;
4106       }
4107       _media_replace_direction (media, answer_dir);
4108
4109       if (!trans->stream) {
4110         TransportStream *item;
4111
4112         item =
4113             _get_or_create_transport_stream (webrtc,
4114             bundled_mids ? bundle_idx : i, FALSE);
4115         webrtc_transceiver_set_transport (trans, item);
4116       }
4117
4118       if (bundled_mids) {
4119         const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
4120
4121         g_assert (mid);
4122         g_string_append_printf (bundled_mids, " %s", mid);
4123       }
4124
4125       /* set the a=fingerprint: for this transport */
4126       _add_fingerprint_to_media (trans->stream->transport, media);
4127
4128       gst_caps_unref (offer_caps);
4129     } else {
4130       GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
4131       goto rejected;
4132     }
4133
4134     if (0) {
4135     rejected:
4136       if (error && *error)
4137         GST_INFO_OBJECT (webrtc, "media %u rejected: %s", i, (*error)->message);
4138       else
4139         GST_INFO_OBJECT (webrtc, "media %u rejected", i);
4140       gst_sdp_media_free (media);
4141       gst_sdp_media_copy (offer_media, &media);
4142       gst_sdp_media_set_port_info (media, 0, 0);
4143       /* Clear error here as it is not propagated to the caller and the media
4144        * is just skipped, i.e. more iterations are going to happen. */
4145       g_clear_error (error);
4146     }
4147     gst_sdp_message_add_media (ret, media);
4148     gst_sdp_media_free (media);
4149   }
4150
4151   if (bundled_mids) {
4152     gchar *mids = g_string_free (bundled_mids, FALSE);
4153
4154     gst_sdp_message_add_attribute (ret, "group", mids);
4155     g_free (mids);
4156   }
4157
4158   if (bundle_ufrag)
4159     g_free (bundle_ufrag);
4160
4161   if (bundle_pwd)
4162     g_free (bundle_pwd);
4163
4164   /* FIXME: can we add not matched transceivers? */
4165
4166   /* XXX: only true for the initial offerer */
4167   gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE);
4168
4169 out:
4170   g_strfreev (bundled);
4171
4172   g_list_free (seen_transceivers);
4173
4174   if (webrtc->priv->last_generated_offer)
4175     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
4176   webrtc->priv->last_generated_offer = NULL;
4177   if (webrtc->priv->last_generated_answer)
4178     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
4179   {
4180     GstSDPMessage *copy;
4181     gst_sdp_message_copy (ret, &copy);
4182     webrtc->priv->last_generated_answer =
4183         gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
4184   }
4185
4186   return ret;
4187 }
4188
4189 struct create_sdp
4190 {
4191   GstStructure *options;
4192   GstWebRTCSDPType type;
4193 };
4194
4195 static GstStructure *
4196 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
4197 {
4198   GstWebRTCSessionDescription *desc = NULL;
4199   GstSDPMessage *sdp = NULL;
4200   GstStructure *s = NULL;
4201   GError *error = NULL;
4202
4203   GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
4204       gst_webrtc_sdp_type_to_string (data->type), data->options);
4205
4206   if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
4207     sdp = _create_offer_task (webrtc, data->options, &error);
4208   else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
4209     sdp = _create_answer_task (webrtc, data->options, &error);
4210   else {
4211     g_assert_not_reached ();
4212     goto out;
4213   }
4214
4215   if (sdp) {
4216     desc = gst_webrtc_session_description_new (data->type, sdp);
4217     s = gst_structure_new ("application/x-gst-promise",
4218         gst_webrtc_sdp_type_to_string (data->type),
4219         GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
4220   } else {
4221     g_warn_if_fail (error != NULL);
4222     GST_WARNING_OBJECT (webrtc, "returning error: %s",
4223         error ? error->message : "Unknown");
4224     s = gst_structure_new ("application/x-gst-promise",
4225         "error", G_TYPE_ERROR, error, NULL);
4226     g_clear_error (&error);
4227   }
4228
4229 out:
4230
4231   if (desc)
4232     gst_webrtc_session_description_free (desc);
4233
4234   return s;
4235 }
4236
4237 static void
4238 _free_create_sdp_data (struct create_sdp *data)
4239 {
4240   if (data->options)
4241     gst_structure_free (data->options);
4242   g_free (data);
4243 }
4244
4245 static void
4246 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
4247     const GstStructure * options, GstPromise * promise)
4248 {
4249   struct create_sdp *data = g_new0 (struct create_sdp, 1);
4250
4251   if (options)
4252     data->options = gst_structure_copy (options);
4253   data->type = GST_WEBRTC_SDP_TYPE_OFFER;
4254
4255   if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4256           data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4257     GError *error =
4258         g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
4259         "Could not create offer. webrtcbin is closed");
4260     GstStructure *s = gst_structure_new ("application/x-gst-promise",
4261         "error", G_TYPE_ERROR, error, NULL);
4262
4263     gst_promise_reply (promise, s);
4264
4265     g_clear_error (&error);
4266   }
4267 }
4268
4269 static void
4270 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
4271     const GstStructure * options, GstPromise * promise)
4272 {
4273   struct create_sdp *data = g_new0 (struct create_sdp, 1);
4274
4275   if (options)
4276     data->options = gst_structure_copy (options);
4277   data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
4278
4279   if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
4280           data, (GDestroyNotify) _free_create_sdp_data, promise)) {
4281     GError *error =
4282         g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
4283         "Could not create answer. webrtcbin is closed.");
4284     GstStructure *s = gst_structure_new ("application/x-gst-promise",
4285         "error", G_TYPE_ERROR, error, NULL);
4286
4287     gst_promise_reply (promise, s);
4288
4289     g_clear_error (&error);
4290   }
4291 }
4292
4293 static GstWebRTCBinPad *
4294 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
4295     GstWebRTCRTPTransceiver * trans, guint serial)
4296 {
4297   GstWebRTCBinPad *pad;
4298   gchar *pad_name;
4299
4300   if (direction == GST_PAD_SINK) {
4301     if (serial == G_MAXUINT)
4302       serial = webrtc->priv->max_sink_pad_serial++;
4303   } else {
4304     serial = trans->mline;
4305   }
4306
4307   pad_name =
4308       g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
4309       serial);
4310   pad = gst_webrtc_bin_pad_new (pad_name, direction);
4311   g_free (pad_name);
4312
4313   pad->trans = gst_object_ref (trans);
4314
4315   return pad;
4316 }
4317
4318 static GstWebRTCRTPTransceiver *
4319 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
4320     const GstSDPMessage * sdp, guint media_idx)
4321 {
4322   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4323   GstWebRTCRTPTransceiver *ret = NULL;
4324   int i;
4325
4326   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4327     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4328
4329     if (g_strcmp0 (attr->key, "mid") == 0) {
4330       if ((ret =
4331               _find_transceiver (webrtc, attr->value,
4332                   (FindTransceiverFunc) match_for_mid)))
4333         goto out;
4334     }
4335   }
4336
4337   ret = _find_transceiver (webrtc, &media_idx,
4338       (FindTransceiverFunc) transceiver_match_for_mline);
4339
4340 out:
4341   GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
4342   return ret;
4343 }
4344
4345 static GstElement *
4346 _build_fec_encoder (GstWebRTCBin * webrtc, WebRTCTransceiver * trans)
4347 {
4348   GstElement *ret = NULL;
4349   GstElement *prev = NULL;
4350   guint ulpfec_pt = 0;
4351   guint red_pt = 0;
4352   GstPad *sinkpad = NULL;
4353   GstWebRTCRTPTransceiver *rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4354
4355   if (trans->stream) {
4356     ulpfec_pt =
4357         transport_stream_get_pt (trans->stream, "ULPFEC", rtp_trans->mline);
4358     red_pt = transport_stream_get_pt (trans->stream, "RED", rtp_trans->mline);
4359   }
4360
4361   if (ulpfec_pt || red_pt)
4362     ret = gst_bin_new (NULL);
4363
4364   if (ulpfec_pt) {
4365     GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4366     GstCaps *caps = transport_stream_get_caps_for_pt (trans->stream, ulpfec_pt);
4367
4368     GST_DEBUG_OBJECT (webrtc,
4369         "Creating ULPFEC encoder for mline %u with pt %d", rtp_trans->mline,
4370         ulpfec_pt);
4371
4372     gst_bin_add (GST_BIN (ret), fecenc);
4373     sinkpad = gst_element_get_static_pad (fecenc, "sink");
4374     g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
4375         trans->fec_percentage, NULL);
4376
4377     g_object_bind_property (rtp_trans, "fec-percentage", fecenc, "percentage",
4378         G_BINDING_BIDIRECTIONAL);
4379
4380     if (caps && !gst_caps_is_empty (caps)) {
4381       const GstStructure *s = gst_caps_get_structure (caps, 0);
4382       const gchar *media = gst_structure_get_string (s, "media");
4383
4384       if (!g_strcmp0 (media, "video"))
4385         g_object_set (fecenc, "multipacket", TRUE, NULL);
4386     }
4387
4388     prev = fecenc;
4389   }
4390
4391   if (red_pt) {
4392     GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
4393
4394     GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for mline %u with pt %d",
4395         rtp_trans->mline, red_pt);
4396
4397     gst_bin_add (GST_BIN (ret), redenc);
4398     if (prev)
4399       gst_element_link (prev, redenc);
4400     else
4401       sinkpad = gst_element_get_static_pad (redenc, "sink");
4402
4403     g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
4404
4405     prev = redenc;
4406   }
4407
4408   if (sinkpad) {
4409     GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
4410     gst_object_unref (sinkpad);
4411     gst_element_add_pad (ret, ghost);
4412   }
4413
4414   if (prev) {
4415     GstPad *srcpad = gst_element_get_static_pad (prev, "src");
4416     GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
4417     gst_object_unref (srcpad);
4418     gst_element_add_pad (ret, ghost);
4419   }
4420
4421   return ret;
4422 }
4423
4424
4425 static GstPad *
4426 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
4427 {
4428 /*
4429  * Not-bundle case:
4430  *
4431  * ,--------------------------------------------webrtcbin--------------------------------------------,
4432  * ;                                                                                                 ;
4433  * ;                                                ,-------rtpbin-------,   ,--transport_send_%u--, ;
4434  * ;                                                ;    send_rtp_src_%u o---o rtp_sink            ; ;
4435  * ;         ,---clocksync---,                      ;                    ;   ;                     ; ;
4436  * ;         ;               ;                      ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
4437  * ; sink_%u ;               ; ,---fec encoder---,  ;                    ;   '---------------------' ;
4438  * o---------o sink      src o-o sink        src o--o send_rtp_sink_%u   ;                           ;
4439  * ;         '---------------' ,-----------------,  '--------------------'                           ;
4440  * '-------------------------------------------------------------------------------------------------'
4441  */
4442
4443 /*
4444  * Bundle case:
4445  * ,-----------------------------------------------------webrtcbin---------------------------------------------------,
4446  * ;                                                                                                                 ;
4447  * ;                                                                ,-------rtpbin-------,   ,--transport_send_%u--, ;
4448  * ;                                                                ;    send_rtp_src_%u o---o rtp_sink            ; ;
4449  * ;                                                                ;                    ;   ;                     ; ;
4450  * ; sink_%u  ,---clocksync---, ,---fec encoder---,  ,---funnel---, ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
4451  * o----------o sink      src o-o sink        src o--o sink_%u    ; ;                    ;   '---------------------' ;
4452  * ;          '---------------' ,-----------------,  ;            ; ;                    ;                           ;
4453  * ;                                                 ;        src o-o send_rtp_sink_%u   ;                           ;
4454  * ; sink_%u  ,---clocksync---, ,---fec encoder---,  ;            ; ;                    ;                           ;
4455  * o----------o sink      src o-o sink        src o--o sink%u     ; '--------------------'                           ;
4456  * ;          '---------------' ,-----------------,  '------------'                                                  ;
4457  * '-----------------------------------------------------------------------------------------------------------------'
4458  */
4459   GstPadTemplate *rtp_templ;
4460   GstPad *rtp_sink, *sinkpad, *srcpad;
4461   gchar *pad_name;
4462   WebRTCTransceiver *trans;
4463   GstElement *clocksync;
4464   GstElement *fec_encoder;
4465
4466   g_return_val_if_fail (pad->trans != NULL, NULL);
4467
4468   trans = WEBRTC_TRANSCEIVER (pad->trans);
4469
4470   GST_INFO_OBJECT (pad, "linking input stream %u", pad->trans->mline);
4471
4472   g_assert (trans->stream);
4473
4474   clocksync = gst_element_factory_make ("clocksync", NULL);
4475   g_object_set (clocksync, "sync", TRUE, NULL);
4476   gst_bin_add (GST_BIN (webrtc), clocksync);
4477   gst_element_sync_state_with_parent (clocksync);
4478
4479   srcpad = gst_element_get_static_pad (clocksync, "src");
4480   sinkpad = gst_element_get_static_pad (clocksync, "sink");
4481
4482   if ((fec_encoder = _build_fec_encoder (webrtc, trans))) {
4483     GstPad *fec_sink;
4484
4485     gst_bin_add (GST_BIN (webrtc), fec_encoder);
4486     gst_element_sync_state_with_parent (fec_encoder);
4487
4488     fec_sink = gst_element_get_static_pad (fec_encoder, "sink");
4489     gst_pad_link (srcpad, fec_sink);
4490     gst_object_unref (srcpad);
4491     gst_object_unref (fec_sink);
4492     srcpad = gst_element_get_static_pad (fec_encoder, "src");
4493   }
4494
4495   if (!webrtc->rtpfunnel) {
4496     rtp_templ =
4497         _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
4498         "send_rtp_sink_%u");
4499     g_assert (rtp_templ);
4500
4501     pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->trans->mline);
4502     rtp_sink =
4503         gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
4504     g_free (pad_name);
4505     gst_pad_link (srcpad, rtp_sink);
4506     gst_object_unref (rtp_sink);
4507
4508     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
4509
4510     pad_name = g_strdup_printf ("send_rtp_src_%u", pad->trans->mline);
4511     if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
4512             GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
4513       g_warn_if_reached ();
4514     g_free (pad_name);
4515   } else {
4516     gchar *pad_name = g_strdup_printf ("sink_%u", pad->trans->mline);
4517     GstPad *funnel_sinkpad =
4518         gst_element_request_pad_simple (webrtc->rtpfunnel, pad_name);
4519
4520     gst_pad_link (srcpad, funnel_sinkpad);
4521     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), sinkpad);
4522
4523     g_free (pad_name);
4524     gst_object_unref (funnel_sinkpad);
4525   }
4526
4527   gst_object_unref (srcpad);
4528   gst_object_unref (sinkpad);
4529
4530   gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
4531
4532   return GST_PAD (pad);
4533 }
4534
4535 /* output pads are receiving elements */
4536 static void
4537 _connect_output_stream (GstWebRTCBin * webrtc,
4538     TransportStream * stream, guint session_id)
4539 {
4540 /*
4541  * ,------------------------webrtcbin------------------------,
4542  * ;                             ,---------rtpbin---------,  ;
4543  * ; ,-transport_receive_%u--,   ;                        ;  ;
4544  * ; ;               rtp_src o---o recv_rtp_sink_%u       ;  ;
4545  * ; ;                       ;   ;                        ;  ;
4546  * ; ;              rtcp_src o---o recv_rtcp_sink_%u      ;  ;
4547  * ; '-----------------------'   ;                        ;  ; src_%u
4548  * ;                             ;  recv_rtp_src_%u_%u_%u o--o
4549  * ;                             '------------------------'  ;
4550  * '---------------------------------------------------------'
4551  */
4552   gchar *pad_name;
4553
4554   if (stream->output_connected) {
4555     GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
4556         "connected to rtpbin.  Not connecting", stream);
4557     return;
4558   }
4559
4560   GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
4561       session_id, stream);
4562
4563   pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
4564   if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
4565           "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
4566     g_warn_if_reached ();
4567   g_free (pad_name);
4568
4569   gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
4570
4571   /* The webrtcbin src_%u output pads will be created when rtpbin receives
4572    * data on that stream in on_rtpbin_pad_added() */
4573
4574   stream->output_connected = TRUE;
4575 }
4576
4577 typedef struct
4578 {
4579   guint mlineindex;
4580   gchar *candidate;
4581 } IceCandidateItem;
4582
4583 static void
4584 _clear_ice_candidate_item (IceCandidateItem * item)
4585 {
4586   g_free (item->candidate);
4587 }
4588
4589 static void
4590 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
4591     gboolean drop_invalid)
4592 {
4593   GstWebRTCICEStream *stream;
4594
4595   stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
4596   if (stream == NULL) {
4597     if (drop_invalid) {
4598       GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
4599           item->mlineindex);
4600     } else {
4601       IceCandidateItem new;
4602       new.mlineindex = item->mlineindex;
4603       new.candidate = g_strdup (item->candidate);
4604       GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
4605
4606       ICE_LOCK (webrtc);
4607       g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
4608       ICE_UNLOCK (webrtc);
4609     }
4610     return;
4611   }
4612
4613   GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4614       item->mlineindex, item->candidate);
4615
4616   gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
4617 }
4618
4619 static void
4620 _add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex,
4621     const GstSDPMedia * media)
4622 {
4623   gint a;
4624   GstWebRTCICEStream *stream = NULL;
4625
4626   for (a = 0; a < gst_sdp_media_attributes_len (media); a++) {
4627     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a);
4628     if (g_strcmp0 (attr->key, "candidate") == 0) {
4629       gchar *candidate;
4630
4631       if (stream == NULL)
4632         stream = _find_ice_stream_for_session (webrtc, mlineindex);
4633       if (stream == NULL) {
4634         GST_DEBUG_OBJECT (webrtc,
4635             "Unknown mline %u, dropping ICE candidates from SDP", mlineindex);
4636         return;
4637       }
4638
4639       candidate = g_strdup_printf ("a=candidate:%s", attr->value);
4640       GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
4641           mlineindex, candidate);
4642       gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate);
4643       g_free (candidate);
4644     }
4645   }
4646 }
4647
4648 static void
4649 _add_ice_candidate_to_sdp (GstWebRTCBin * webrtc,
4650     GstSDPMessage * sdp, gint mline_index, const gchar * candidate)
4651 {
4652   GstSDPMedia *media = NULL;
4653
4654   if (mline_index < sdp->medias->len) {
4655     media = &g_array_index (sdp->medias, GstSDPMedia, mline_index);
4656   }
4657
4658   if (media == NULL) {
4659     GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate",
4660         mline_index);
4661     return;
4662   }
4663   // Add the candidate as an attribute, first stripping off the existing
4664   // candidate: key from the string description
4665   if (strlen (candidate) < 10) {
4666     GST_WARNING_OBJECT (webrtc,
4667         "Dropping invalid ICE candidate for mline %d: %s", mline_index,
4668         candidate);
4669     return;
4670   }
4671   gst_sdp_media_add_attribute (media, "candidate", candidate + 10);
4672 }
4673
4674 static gboolean
4675 _filter_sdp_fields (GQuark field_id, const GValue * value,
4676     GstStructure * new_structure)
4677 {
4678   if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
4679     gst_structure_id_set_value (new_structure, field_id, value);
4680   }
4681   return TRUE;
4682 }
4683
4684 static void
4685 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
4686 {
4687   gint *rtx_pt;
4688   gsize rtx_count;
4689
4690   rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
4691   GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
4692   if (rtx_pt) {
4693     GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4694     gsize i;
4695
4696     for (i = 0; i < rtx_count; i++) {
4697       GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
4698       const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4699       const gchar *apt = gst_structure_get_string (s, "apt");
4700
4701       GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
4702       gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
4703     }
4704
4705     GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
4706         GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
4707         stream->rtxsend, pt_map);
4708
4709     if (stream->rtxreceive)
4710       g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
4711     if (stream->rtxsend)
4712       g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
4713
4714     gst_structure_free (pt_map);
4715   }
4716 }
4717
4718 static void
4719 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
4720     TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
4721 {
4722   guint i, len;
4723   const gchar *proto;
4724   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4725
4726   /* get proto */
4727   proto = gst_sdp_media_get_proto (media);
4728   if (proto != NULL) {
4729     /* Parse global SDP attributes once */
4730     GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
4731     GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
4732     gst_sdp_message_attributes_to_caps (sdp, global_caps);
4733     GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
4734     gst_sdp_media_attributes_to_caps (media, global_caps);
4735
4736     len = gst_sdp_media_formats_len (media);
4737     for (i = 0; i < len; i++) {
4738       GstCaps *caps, *outcaps;
4739       GstStructure *s;
4740       PtMapItem item;
4741       gint pt;
4742       guint j;
4743
4744       pt = atoi (gst_sdp_media_get_format (media, i));
4745
4746       GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
4747
4748       /* convert caps */
4749       caps = gst_sdp_media_get_caps_from_media (media, pt);
4750       if (caps == NULL) {
4751         GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
4752         continue;
4753       }
4754
4755       /* Merge in global caps */
4756       /* Intersect will merge in missing fields to the current caps */
4757       outcaps = gst_caps_intersect (caps, global_caps);
4758       gst_caps_unref (caps);
4759
4760       s = gst_caps_get_structure (outcaps, 0);
4761       gst_structure_set_name (s, "application/x-rtp");
4762       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
4763         gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
4764
4765       item.caps = gst_caps_new_empty ();
4766
4767       for (j = 0; j < gst_caps_get_size (outcaps); j++) {
4768         GstStructure *s = gst_caps_get_structure (outcaps, j);
4769         GstStructure *filtered =
4770             gst_structure_new_empty (gst_structure_get_name (s));
4771
4772         gst_structure_foreach (s,
4773             (GstStructureForeachFunc) _filter_sdp_fields, filtered);
4774         gst_caps_append_structure (item.caps, filtered);
4775       }
4776
4777       item.pt = pt;
4778       item.media_idx = media_idx;
4779       gst_caps_unref (outcaps);
4780
4781       g_array_append_val (stream->ptmap, item);
4782     }
4783
4784     gst_caps_unref (global_caps);
4785   }
4786 }
4787
4788 static void
4789 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
4790     const GstSDPMessage * sdp, guint media_idx,
4791     TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
4792     GStrv bundled, guint bundle_idx, GError ** error)
4793 {
4794   WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
4795   GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
4796   GstWebRTCRTPTransceiverDirection new_dir;
4797   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
4798   GstWebRTCDTLSSetup new_setup;
4799   gboolean new_rtcp_rsize;
4800   ReceiveState receive_state = RECEIVE_STATE_UNSET;
4801   int i;
4802
4803   rtp_trans->mline = media_idx;
4804
4805   if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
4806     if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
4807       GST_FIXME_OBJECT (webrtc,
4808           "Updating video transceiver to audio, which isn't fully supported.");
4809     rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
4810   }
4811
4812   if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
4813     if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
4814       GST_FIXME_OBJECT (webrtc,
4815           "Updating audio transceiver to video, which isn't fully supported.");
4816     rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
4817   }
4818
4819   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
4820     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
4821
4822     if (g_strcmp0 (attr->key, "mid") == 0) {
4823       g_free (rtp_trans->mid);
4824       rtp_trans->mid = g_strdup (attr->value);
4825     }
4826   }
4827
4828   {
4829     const GstSDPMedia *local_media, *remote_media;
4830     GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
4831     GstWebRTCDTLSSetup local_setup, remote_setup;
4832
4833     local_media =
4834         gst_sdp_message_get_media (webrtc->current_local_description->sdp,
4835         media_idx);
4836     remote_media =
4837         gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
4838         media_idx);
4839
4840     local_setup = _get_dtls_setup_from_media (local_media);
4841     remote_setup = _get_dtls_setup_from_media (remote_media);
4842     new_setup = _get_final_setup (local_setup, remote_setup);
4843     if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
4844       g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4845           "Cannot intersect direction attributes for media %u", media_idx);
4846       return;
4847     }
4848
4849     local_dir = _get_direction_from_media (local_media);
4850     remote_dir = _get_direction_from_media (remote_media);
4851     new_dir = _get_final_direction (local_dir, remote_dir);
4852     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
4853       g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
4854           "Cannot intersect dtls setup attributes for media %u", media_idx);
4855       return;
4856     }
4857
4858     if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
4859         && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
4860         && prev_dir != new_dir) {
4861       g_set_error (error, GST_WEBRTC_BIN_ERROR,
4862           GST_WEBRTC_BIN_ERROR_NOT_IMPLEMENTED,
4863           "transceiver direction changes are not implemented. Media %u",
4864           media_idx);
4865       return;
4866     }
4867
4868     if (!bundled || bundle_idx == media_idx) {
4869       new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
4870           && _media_has_attribute_key (remote_media, "rtcp-rsize");
4871
4872       {
4873         GObject *session;
4874         g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
4875             media_idx, &session);
4876         if (session) {
4877           g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
4878           g_object_unref (session);
4879         }
4880       }
4881     }
4882   }
4883
4884   if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4885     if (!bundled) {
4886       /* Not a bundled stream means this entire transport is inactive,
4887        * so set the receive state to BLOCK below */
4888       stream->active = FALSE;
4889       receive_state = RECEIVE_STATE_BLOCK;
4890     }
4891   } else {
4892     /* If this transceiver is active for sending or receiving,
4893      * we still need receive at least RTCP, so need to unblock
4894      * the receive bin below. */
4895     GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream);
4896     receive_state = RECEIVE_STATE_PASS;
4897     stream->active = TRUE;
4898   }
4899
4900   if (new_dir != prev_dir) {
4901     gchar *prev_dir_s, *new_dir_s;
4902
4903     prev_dir_s =
4904         _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4905         prev_dir);
4906     new_dir_s =
4907         _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
4908         new_dir);
4909
4910     GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT
4911         " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s);
4912
4913     g_free (prev_dir_s);
4914     prev_dir_s = NULL;
4915     g_free (new_dir_s);
4916     new_dir_s = NULL;
4917
4918     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
4919       GstWebRTCBinPad *pad;
4920
4921       pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
4922       if (pad) {
4923         GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
4924         if (target) {
4925           GstPad *peer = gst_pad_get_peer (target);
4926           if (peer) {
4927             gst_pad_send_event (peer, gst_event_new_eos ());
4928             gst_object_unref (peer);
4929           }
4930           gst_object_unref (target);
4931         }
4932         gst_object_unref (pad);
4933       }
4934
4935       /* XXX: send eos event up the sink pad as well? */
4936     }
4937
4938     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
4939         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4940       GstWebRTCBinPad *pad =
4941           _find_pad_for_transceiver (webrtc, GST_PAD_SINK, rtp_trans);
4942       if (pad) {
4943         GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
4944             " for transceiver %" GST_PTR_FORMAT, pad, trans);
4945         gst_object_unref (pad);
4946       } else {
4947         GST_DEBUG_OBJECT (webrtc,
4948             "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
4949         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, rtp_trans,
4950             G_MAXUINT);
4951         _connect_input_stream (webrtc, pad);
4952         _add_pad (webrtc, pad);
4953       }
4954     }
4955     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
4956         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
4957       GstWebRTCBinPad *pad =
4958           _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4959       if (pad) {
4960         GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
4961             " for transceiver %" GST_PTR_FORMAT, pad, trans);
4962         gst_object_unref (pad);
4963       } else {
4964         GST_DEBUG_OBJECT (webrtc,
4965             "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
4966         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans,
4967             G_MAXUINT);
4968
4969         if (!trans->stream) {
4970           TransportStream *item;
4971
4972           item =
4973               _get_or_create_transport_stream (webrtc,
4974               bundled ? bundle_idx : media_idx, FALSE);
4975           webrtc_transceiver_set_transport (trans, item);
4976         }
4977
4978         _connect_output_stream (webrtc, trans->stream,
4979             bundled ? bundle_idx : media_idx);
4980         /* delay adding the pad until rtpbin creates the recv output pad
4981          * to ghost to so queries/events travel through the pipeline correctly
4982          * as soon as the pad is added */
4983         _add_pad_to_list (webrtc, pad);
4984       }
4985
4986     }
4987
4988     rtp_trans->mline = media_idx;
4989     rtp_trans->current_direction = new_dir;
4990   }
4991
4992   if (!bundled || bundle_idx == media_idx) {
4993     if (stream->rtxsend || stream->rtxreceive) {
4994       _set_rtx_ptmap_from_stream (webrtc, stream);
4995     }
4996
4997     g_object_set (stream, "dtls-client",
4998         new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
4999   }
5000
5001   /* Must be after setting the "dtls-client" so that data is not pushed into
5002    * the dtlssrtp elements before the ssl direction has been set which will
5003    * throw SSL errors */
5004   if (receive_state != RECEIVE_STATE_UNSET)
5005     transport_receive_bin_set_receive_state (stream->receive_bin,
5006         receive_state);
5007 }
5008
5009 /* must be called with the pc lock held */
5010 static gint
5011 _generate_data_channel_id (GstWebRTCBin * webrtc)
5012 {
5013   gboolean is_client;
5014   gint new_id = -1, max_channels = 0;
5015
5016   if (webrtc->priv->sctp_transport) {
5017     g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
5018         NULL);
5019   }
5020   if (max_channels <= 0) {
5021     max_channels = 65534;
5022   }
5023
5024   g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
5025       NULL);
5026
5027   /* TODO: a better search algorithm */
5028   do {
5029     WebRTCDataChannel *channel;
5030
5031     new_id++;
5032
5033     if (new_id < 0 || new_id >= max_channels) {
5034       /* exhausted id space */
5035       GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
5036           "data channel id (max %i)", max_channels);
5037       return -1;
5038     }
5039
5040     /* client must generate even ids, server must generate odd ids */
5041     if (new_id % 2 == ! !is_client)
5042       continue;
5043
5044     channel = _find_data_channel_for_id (webrtc, new_id);
5045     if (!channel)
5046       break;
5047   } while (TRUE);
5048
5049   return new_id;
5050 }
5051
5052 static void
5053 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
5054     const GstSDPMessage * sdp, guint media_idx, TransportStream * stream,
5055     GError ** error)
5056 {
5057   const GstSDPMedia *local_media, *remote_media;
5058   GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
5059   TransportReceiveBin *receive;
5060   int local_port, remote_port;
5061   guint64 local_max_size, remote_max_size, max_size;
5062   int i;
5063
5064   local_media =
5065       gst_sdp_message_get_media (webrtc->current_local_description->sdp,
5066       media_idx);
5067   remote_media =
5068       gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
5069       media_idx);
5070
5071   local_setup = _get_dtls_setup_from_media (local_media);
5072   remote_setup = _get_dtls_setup_from_media (remote_media);
5073   new_setup = _get_final_setup (local_setup, remote_setup);
5074   if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
5075     g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5076         "Cannot intersect dtls setup for media %u", media_idx);
5077     return;
5078   }
5079
5080   /* data channel is always rtcp-muxed to avoid generating ICE candidates
5081    * for RTCP */
5082   g_object_set (stream, "dtls-client",
5083       new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
5084
5085   local_port = _get_sctp_port_from_media (local_media);
5086   remote_port = _get_sctp_port_from_media (local_media);
5087   if (local_port == -1 || remote_port == -1) {
5088     g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5089         "Could not find sctp port for media %u (local %i, remote %i)",
5090         media_idx, local_port, remote_port);
5091     return;
5092   }
5093
5094   if (0 == (local_max_size =
5095           _get_sctp_max_message_size_from_media (local_media)))
5096     local_max_size = G_MAXUINT64;
5097   if (0 == (remote_max_size =
5098           _get_sctp_max_message_size_from_media (remote_media)))
5099     remote_max_size = G_MAXUINT64;
5100   max_size = MIN (local_max_size, remote_max_size);
5101
5102   webrtc->priv->sctp_transport->max_message_size = max_size;
5103
5104   {
5105     guint orig_local_port, orig_remote_port;
5106
5107     /* XXX: sctpassociation warns if we are in the wrong state */
5108     g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5109         &orig_local_port, NULL);
5110
5111     if (orig_local_port != local_port)
5112       g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
5113           local_port, NULL);
5114
5115     g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5116         &orig_remote_port, NULL);
5117     if (orig_remote_port != remote_port)
5118       g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
5119           remote_port, NULL);
5120   }
5121
5122   DC_LOCK (webrtc);
5123   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
5124     WebRTCDataChannel *channel;
5125
5126     channel = g_ptr_array_index (webrtc->priv->data_channels, i);
5127
5128     if (channel->parent.id == -1)
5129       channel->parent.id = _generate_data_channel_id (webrtc);
5130     if (channel->parent.id == -1)
5131       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
5132           ("%s", "Failed to generate an identifier for a data channel"), NULL);
5133
5134     if (webrtc->priv->sctp_transport->association_established
5135         && !channel->parent.negotiated && !channel->opened) {
5136       webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport);
5137       webrtc_data_channel_start_negotiation (channel);
5138     }
5139   }
5140   DC_UNLOCK (webrtc);
5141
5142   stream->active = TRUE;
5143
5144   receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
5145   transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
5146 }
5147
5148 static gboolean
5149 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
5150     gconstpointer data)
5151 {
5152   GstWebRTCKind kind = GPOINTER_TO_INT (data);
5153
5154   if (p1->mid)
5155     return FALSE;
5156   if (p1->mline != -1)
5157     return FALSE;
5158   if (p1->stopped)
5159     return FALSE;
5160   if (p1->kind != GST_WEBRTC_KIND_UNKNOWN && p1->kind != kind)
5161     return FALSE;
5162
5163   return TRUE;
5164 }
5165
5166 static void
5167 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
5168 {
5169   gchar *pad_name;
5170   GstPad *queue_srcpad;
5171   GstPad *rtp_sink;
5172   TransportStream *stream = _find_transport_for_session (webrtc, session_id);
5173   GstElement *queue;
5174
5175   g_assert (stream);
5176
5177   if (webrtc->rtpfunnel)
5178     goto done;
5179
5180   webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
5181   gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
5182   gst_element_sync_state_with_parent (webrtc->rtpfunnel);
5183
5184   queue = gst_element_factory_make ("queue", NULL);
5185   gst_bin_add (GST_BIN (webrtc), queue);
5186   gst_element_sync_state_with_parent (queue);
5187
5188   gst_element_link (webrtc->rtpfunnel, queue);
5189
5190   queue_srcpad = gst_element_get_static_pad (queue, "src");
5191
5192   pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
5193   rtp_sink = gst_element_request_pad_simple (webrtc->rtpbin, pad_name);
5194   g_free (pad_name);
5195   gst_pad_link (queue_srcpad, rtp_sink);
5196   gst_object_unref (queue_srcpad);
5197   gst_object_unref (rtp_sink);
5198
5199   pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
5200   if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
5201           GST_ELEMENT (stream->send_bin), "rtp_sink"))
5202     g_warn_if_reached ();
5203   g_free (pad_name);
5204
5205 done:
5206   return;
5207 }
5208
5209 static gboolean
5210 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
5211     GstWebRTCSessionDescription * sdp, GError ** error)
5212 {
5213   int i;
5214   gboolean ret = FALSE;
5215   GStrv bundled = NULL;
5216   guint bundle_idx = 0;
5217   TransportStream *bundle_stream = NULL;
5218
5219   /* FIXME: With some peers, it's possible we could have
5220    * multiple bundles to deal with, although I've never seen one yet */
5221   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5222     if (!_parse_bundle (sdp->sdp, &bundled, error))
5223       goto done;
5224
5225   if (bundled) {
5226
5227     if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
5228       g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5229           "Bundle tag is %s but no media found matching", bundled[0]);
5230       goto done;
5231     }
5232
5233     bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
5234         _message_media_is_datachannel (sdp->sdp, bundle_idx));
5235     /* Mark the bundle stream as inactive to start. It will be set to TRUE
5236      * by any bundled mline that is active, and at the end we set the
5237      * receivebin to BLOCK if all mlines were inactive. */
5238     bundle_stream->active = FALSE;
5239
5240     g_array_set_size (bundle_stream->ptmap, 0);
5241     for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5242       /* When bundling, we need to do this up front, or else RTX
5243        * parameters aren't set up properly for the bundled streams */
5244       _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
5245     }
5246
5247     _connect_rtpfunnel (webrtc, bundle_idx);
5248   }
5249
5250   for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5251     const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5252     TransportStream *stream;
5253     GstWebRTCRTPTransceiver *trans;
5254     guint transport_idx;
5255
5256     /* skip rejected media */
5257     if (gst_sdp_media_get_port (media) == 0)
5258       continue;
5259
5260     if (bundled)
5261       transport_idx = bundle_idx;
5262     else
5263       transport_idx = i;
5264
5265     trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5266
5267     stream = _get_or_create_transport_stream (webrtc, transport_idx,
5268         _message_media_is_datachannel (sdp->sdp, transport_idx));
5269     if (!bundled) {
5270       /* When bundling, these were all set up above, but when not
5271        * bundling we need to do it now */
5272       g_array_set_size (stream->ptmap, 0);
5273       _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
5274     }
5275
5276     if (trans)
5277       webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
5278
5279     if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
5280       g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5281           "State mismatch.  Could not find local transceiver by mline %u", i);
5282       goto done;
5283     } else {
5284       if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
5285           g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
5286         GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
5287
5288         /* No existing transceiver, find an unused one */
5289         if (!trans) {
5290           if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0)
5291             kind = GST_WEBRTC_KIND_AUDIO;
5292           else if (g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0)
5293             kind = GST_WEBRTC_KIND_VIDEO;
5294           else
5295             GST_LOG_OBJECT (webrtc, "Unknown media kind %s",
5296                 GST_STR_NULL (gst_sdp_media_get_media (media)));
5297
5298           trans = _find_transceiver (webrtc, GINT_TO_POINTER (kind),
5299               (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
5300         }
5301
5302         /* Still no transceiver? Create one */
5303         /* XXX: default to the advertised direction in the sdp for new
5304          * transceivers.  The spec doesn't actually say what happens here, only
5305          * that calls to setDirection will change the value.  Nothing about
5306          * a default value when the transceiver is created internally */
5307         if (!trans) {
5308           WebRTCTransceiver *t = _create_webrtc_transceiver (webrtc,
5309               _get_direction_from_media (media), i, kind, NULL);
5310           webrtc_transceiver_set_transport (t, stream);
5311           trans = GST_WEBRTC_RTP_TRANSCEIVER (t);
5312         }
5313
5314         _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
5315             trans, bundled, bundle_idx, error);
5316         if (error && *error)
5317           goto done;
5318       } else if (_message_media_is_datachannel (sdp->sdp, i)) {
5319         _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream,
5320             error);
5321         if (error && *error)
5322           goto done;
5323       } else {
5324         GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
5325       }
5326     }
5327   }
5328
5329   if (bundle_stream && bundle_stream->active == FALSE) {
5330     /* No bundled mline marked the bundle as active, so block the receive bin, as
5331      * this bundle is completely inactive */
5332     GST_LOG_OBJECT (webrtc,
5333         "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx);
5334     transport_receive_bin_set_receive_state (bundle_stream->receive_bin,
5335         RECEIVE_STATE_BLOCK);
5336   }
5337
5338   ret = TRUE;
5339
5340 done:
5341   g_strfreev (bundled);
5342
5343   return ret;
5344 }
5345
5346 static gboolean
5347 check_transceivers_not_removed (GstWebRTCBin * webrtc,
5348     GstWebRTCSessionDescription * previous, GstWebRTCSessionDescription * new)
5349 {
5350   if (!previous)
5351     return TRUE;
5352
5353   if (gst_sdp_message_medias_len (previous->sdp) >
5354       gst_sdp_message_medias_len (new->sdp))
5355     return FALSE;
5356
5357   return TRUE;
5358 }
5359
5360 static gboolean
5361 check_locked_mlines (GstWebRTCBin * webrtc, GstWebRTCSessionDescription * sdp,
5362     GError ** error)
5363 {
5364   guint i;
5365
5366   for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
5367     const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
5368     GstWebRTCRTPTransceiver *rtp_trans;
5369     WebRTCTransceiver *trans;
5370
5371     rtp_trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
5372     /* only look for matching mid */
5373     if (rtp_trans == NULL)
5374       continue;
5375
5376     trans = WEBRTC_TRANSCEIVER (rtp_trans);
5377
5378     /* We only validate the locked mlines for now */
5379     if (!trans->mline_locked)
5380       continue;
5381
5382     if (rtp_trans->mline != i) {
5383       g_set_error (error, GST_WEBRTC_BIN_ERROR,
5384           GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
5385           "m-line with mid %s is at position %d, but was locked to %d, "
5386           "rejecting", rtp_trans->mid, i, rtp_trans->mline);
5387       return FALSE;
5388     }
5389
5390     if (rtp_trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
5391       if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio") &&
5392           rtp_trans->kind != GST_WEBRTC_KIND_AUDIO) {
5393         g_set_error (error, GST_WEBRTC_BIN_ERROR,
5394             GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
5395             "m-line %d was locked to audio, but SDP has %s media", i,
5396             gst_sdp_media_get_media (media));
5397         return FALSE;
5398       }
5399
5400       if (!g_strcmp0 (gst_sdp_media_get_media (media), "video") &&
5401           rtp_trans->kind != GST_WEBRTC_KIND_VIDEO) {
5402         g_set_error (error, GST_WEBRTC_BIN_ERROR,
5403             GST_WEBRTC_BIN_ERROR_IMPOSSIBLE_MLINE_RESTRICTION,
5404             "m-line %d was locked to video, but SDP has %s media", i,
5405             gst_sdp_media_get_media (media));
5406         return FALSE;
5407       }
5408     }
5409   }
5410
5411   return TRUE;
5412 }
5413
5414
5415 struct set_description
5416 {
5417   SDPSource source;
5418   GstWebRTCSessionDescription *sdp;
5419 };
5420
5421 static GstWebRTCSessionDescription *
5422 get_previous_description (GstWebRTCBin * webrtc, SDPSource source,
5423     GstWebRTCSDPType type)
5424 {
5425   switch (type) {
5426     case GST_WEBRTC_SDP_TYPE_OFFER:
5427     case GST_WEBRTC_SDP_TYPE_PRANSWER:
5428     case GST_WEBRTC_SDP_TYPE_ANSWER:
5429       if (source == SDP_LOCAL) {
5430         return webrtc->current_local_description;
5431       } else {
5432         return webrtc->current_remote_description;
5433       }
5434     case GST_WEBRTC_SDP_TYPE_ROLLBACK:
5435       return NULL;
5436     default:
5437       /* other values mean memory corruption/uninitialized! */
5438       g_assert_not_reached ();
5439       break;
5440   }
5441
5442   return NULL;
5443 }
5444
5445 /* http://w3c.github.io/webrtc-pc/#set-description */
5446 static GstStructure *
5447 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
5448 {
5449   GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
5450   gboolean signalling_state_changed = FALSE;
5451   GError *error = NULL;
5452   GStrv bundled = NULL;
5453   guint bundle_idx = 0;
5454   guint i;
5455
5456   {
5457     gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5458         webrtc->signaling_state);
5459     gchar *type_str =
5460         _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
5461     gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
5462     GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
5463         _sdp_source_to_string (sd->source), type_str, state);
5464     GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
5465     g_free (sdp_text);
5466     g_free (state);
5467     g_free (type_str);
5468   }
5469
5470   if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error))
5471     goto out;
5472
5473   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE)
5474     if (!_parse_bundle (sd->sdp->sdp, &bundled, &error))
5475       goto out;
5476
5477   if (bundled) {
5478     if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
5479       g_set_error (&error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_BAD_SDP,
5480           "Bundle tag is %s but no matching media found", bundled[0]);
5481       goto out;
5482     }
5483   }
5484
5485   if (!check_transceivers_not_removed (webrtc,
5486           get_previous_description (webrtc, sd->source, sd->sdp->type),
5487           sd->sdp)) {
5488     g_set_error_literal (&error, GST_WEBRTC_BIN_ERROR,
5489         GST_WEBRTC_BIN_ERROR_BAD_SDP,
5490         "m=lines removed from the SDP. Processing a completely new connection "
5491         "is not currently supported.");
5492     goto out;
5493   }
5494
5495   if (!check_locked_mlines (webrtc, sd->sdp, &error))
5496     goto out;
5497
5498   switch (sd->sdp->type) {
5499     case GST_WEBRTC_SDP_TYPE_OFFER:{
5500       if (sd->source == SDP_LOCAL) {
5501         if (webrtc->pending_local_description)
5502           gst_webrtc_session_description_free
5503               (webrtc->pending_local_description);
5504         webrtc->pending_local_description =
5505             gst_webrtc_session_description_copy (sd->sdp);
5506         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
5507       } else {
5508         if (webrtc->pending_remote_description)
5509           gst_webrtc_session_description_free
5510               (webrtc->pending_remote_description);
5511         webrtc->pending_remote_description =
5512             gst_webrtc_session_description_copy (sd->sdp);
5513         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
5514       }
5515       break;
5516     }
5517     case GST_WEBRTC_SDP_TYPE_ANSWER:{
5518       if (sd->source == SDP_LOCAL) {
5519         if (webrtc->current_local_description)
5520           gst_webrtc_session_description_free
5521               (webrtc->current_local_description);
5522         webrtc->current_local_description =
5523             gst_webrtc_session_description_copy (sd->sdp);
5524
5525         if (webrtc->current_remote_description)
5526           gst_webrtc_session_description_free
5527               (webrtc->current_remote_description);
5528         webrtc->current_remote_description = webrtc->pending_remote_description;
5529         webrtc->pending_remote_description = NULL;
5530       } else {
5531         if (webrtc->current_remote_description)
5532           gst_webrtc_session_description_free
5533               (webrtc->current_remote_description);
5534         webrtc->current_remote_description =
5535             gst_webrtc_session_description_copy (sd->sdp);
5536
5537         if (webrtc->current_local_description)
5538           gst_webrtc_session_description_free
5539               (webrtc->current_local_description);
5540         webrtc->current_local_description = webrtc->pending_local_description;
5541         webrtc->pending_local_description = NULL;
5542       }
5543
5544       if (webrtc->pending_local_description)
5545         gst_webrtc_session_description_free (webrtc->pending_local_description);
5546       webrtc->pending_local_description = NULL;
5547
5548       if (webrtc->pending_remote_description)
5549         gst_webrtc_session_description_free
5550             (webrtc->pending_remote_description);
5551       webrtc->pending_remote_description = NULL;
5552
5553       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5554       break;
5555     }
5556     case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
5557       GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
5558       if (sd->source == SDP_LOCAL) {
5559         if (webrtc->pending_local_description)
5560           gst_webrtc_session_description_free
5561               (webrtc->pending_local_description);
5562         webrtc->pending_local_description = NULL;
5563       } else {
5564         if (webrtc->pending_remote_description)
5565           gst_webrtc_session_description_free
5566               (webrtc->pending_remote_description);
5567         webrtc->pending_remote_description = NULL;
5568       }
5569
5570       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
5571       break;
5572     }
5573     case GST_WEBRTC_SDP_TYPE_PRANSWER:{
5574       GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
5575       if (sd->source == SDP_LOCAL) {
5576         if (webrtc->pending_local_description)
5577           gst_webrtc_session_description_free
5578               (webrtc->pending_local_description);
5579         webrtc->pending_local_description =
5580             gst_webrtc_session_description_copy (sd->sdp);
5581
5582         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
5583       } else {
5584         if (webrtc->pending_remote_description)
5585           gst_webrtc_session_description_free
5586               (webrtc->pending_remote_description);
5587         webrtc->pending_remote_description =
5588             gst_webrtc_session_description_copy (sd->sdp);
5589
5590         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
5591       }
5592       break;
5593     }
5594   }
5595
5596   if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
5597     /* FIXME:
5598      * If the mid value of an RTCRtpTransceiver was set to a non-null value
5599      * by the RTCSessionDescription that is being rolled back, set the mid
5600      * value of that transceiver to null, as described by [JSEP]
5601      * (section 4.1.7.2.).
5602      * If an RTCRtpTransceiver was created by applying the
5603      * RTCSessionDescription that is being rolled back, and a track has not
5604      * been attached to it via addTrack, remove that transceiver from
5605      * connection's set of transceivers, as described by [JSEP]
5606      * (section 4.1.7.2.).
5607      * Restore the value of connection's [[ sctpTransport]] internal slot
5608      * to its value at the last stable signaling state.
5609      */
5610   }
5611
5612   if (webrtc->signaling_state != new_signaling_state) {
5613     webrtc->signaling_state = new_signaling_state;
5614     signalling_state_changed = TRUE;
5615   }
5616
5617   {
5618     gboolean ice_controller = FALSE;
5619
5620     /* get the current value so we don't change ice controller from TRUE to
5621      * FALSE on renegotiation or once set to TRUE for the initial local offer */
5622     ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice);
5623
5624     /* we control ice negotiation if we send the initial offer */
5625     ice_controller |=
5626         new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER
5627         && webrtc->current_remote_description == NULL;
5628     /* or, if the remote is an ice-lite peer */
5629     ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE
5630         && webrtc->current_remote_description
5631         && _message_has_attribute_key (webrtc->current_remote_description->sdp,
5632         "ice-lite");
5633
5634     GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s",
5635         ice_controller ? "true" : "false");
5636     gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller);
5637   }
5638
5639   if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5640     GList *tmp;
5641
5642     /* media modifications */
5643     if (!_update_transceivers_from_sdp (webrtc, sd->source, sd->sdp, &error))
5644       goto out;
5645
5646     for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) {
5647       GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
5648       GstWebRTCRTPTransceiverDirection new_dir;
5649       GList *old = tmp;
5650       const GstSDPMedia *media;
5651
5652       if (!pad->received_caps) {
5653         GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping.");
5654         tmp = tmp->next;
5655         continue;
5656       }
5657
5658       if (pad->trans->mline >= gst_sdp_message_medias_len (sd->sdp->sdp)) {
5659         GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping");
5660         tmp = tmp->next;
5661         continue;
5662       }
5663
5664       media = gst_sdp_message_get_media (sd->sdp->sdp, pad->trans->mline);
5665       /* skip rejected media */
5666       if (gst_sdp_media_get_port (media) == 0) {
5667         /* FIXME: arrange for an appropriate flow return */
5668         GST_FIXME_OBJECT (pad, "Media has been rejected.  Need to arrange for "
5669             "a more correct flow return.");
5670         tmp = tmp->next;
5671         continue;
5672       }
5673
5674       if (!pad->trans) {
5675         GST_LOG_OBJECT (pad, "doesn't have a transceiver");
5676         tmp = tmp->next;
5677         continue;
5678       }
5679
5680       new_dir = pad->trans->direction;
5681       if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY &&
5682           new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
5683         GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending "
5684             "data at the moment. Not connecting input stream yet", pad->trans);
5685         tmp = tmp->next;
5686         continue;
5687       }
5688
5689       GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with "
5690           "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT,
5691           pad->trans, pad->received_caps);
5692       _connect_input_stream (webrtc, pad);
5693       gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
5694       pad->block_id = 0;
5695
5696       tmp = tmp->next;
5697       gst_object_unref (old->data);
5698       webrtc->priv->pending_sink_transceivers =
5699           g_list_delete_link (webrtc->priv->pending_sink_transceivers, old);
5700     }
5701   }
5702
5703   for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
5704     const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
5705     gchar *ufrag, *pwd;
5706     TransportStream *item;
5707
5708     item =
5709         _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
5710         _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
5711
5712     if (sd->source == SDP_REMOTE) {
5713       guint j;
5714
5715       for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
5716         const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
5717
5718         if (g_strcmp0 (attr->key, "ssrc") == 0) {
5719           GStrv split = g_strsplit (attr->value, " ", 0);
5720           guint32 ssrc;
5721
5722           if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
5723               && g_str_has_prefix (split[1], "cname:")) {
5724             g_ptr_array_add (item->remote_ssrcmap, ssrcmap_item_new (ssrc, i));
5725           }
5726           g_strfreev (split);
5727         }
5728       }
5729     }
5730
5731     if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) {
5732       _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5733
5734       gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
5735           item->stream, ufrag, pwd);
5736       g_free (ufrag);
5737       g_free (pwd);
5738     } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) {
5739       _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
5740
5741       gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
5742           item->stream, ufrag, pwd);
5743       g_free (ufrag);
5744       g_free (pwd);
5745     }
5746   }
5747
5748   if (sd->source == SDP_LOCAL) {
5749     for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
5750       IceStreamItem *item =
5751           &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
5752
5753       gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
5754     }
5755   }
5756
5757   /* Add any pending trickle ICE candidates if we have both offer and answer */
5758   if (webrtc->current_local_description && webrtc->current_remote_description) {
5759     int i;
5760
5761     GstWebRTCSessionDescription *remote_sdp =
5762         webrtc->current_remote_description;
5763
5764     /* Add any remote ICE candidates from the remote description to
5765      * support non-trickle peers first */
5766     for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) {
5767       const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i);
5768       _add_ice_candidates_from_sdp (webrtc, i, media);
5769     }
5770
5771     ICE_LOCK (webrtc);
5772     for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) {
5773       IceCandidateItem *item =
5774           &g_array_index (webrtc->priv->pending_remote_ice_candidates,
5775           IceCandidateItem, i);
5776
5777       _add_ice_candidate (webrtc, item, TRUE);
5778     }
5779     g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0);
5780     ICE_UNLOCK (webrtc);
5781   }
5782
5783   /*
5784    * If connection's signaling state changed above, fire an event named
5785    * signalingstatechange at connection.
5786    */
5787   if (signalling_state_changed) {
5788     gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5789         webrtc->signaling_state);
5790     gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
5791         new_signaling_state);
5792     GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
5793         "to %s", from, to);
5794     PC_UNLOCK (webrtc);
5795     g_object_notify (G_OBJECT (webrtc), "signaling-state");
5796     PC_LOCK (webrtc);
5797
5798     g_free (from);
5799     g_free (to);
5800   }
5801
5802   if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
5803     gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
5804
5805     /* If connection's signaling state is now stable, update the
5806      * negotiation-needed flag. If connection's [[ needNegotiation]] slot
5807      * was true both before and after this update, queue a task to check
5808      * connection's [[needNegotiation]] slot and, if still true, fire a
5809      * simple event named negotiationneeded at connection.*/
5810     _update_need_negotiation (webrtc);
5811     if (prev_need_negotiation && webrtc->priv->need_negotiation) {
5812       _check_need_negotiation_task (webrtc, NULL);
5813     }
5814   }
5815
5816 out:
5817   g_strfreev (bundled);
5818
5819   if (error) {
5820     GstStructure *s = gst_structure_new ("application/x-gst-promise",
5821         "error", G_TYPE_ERROR, error, NULL);
5822     GST_WARNING_OBJECT (webrtc, "returning error: %s", error->message);
5823     g_clear_error (&error);
5824     return s;
5825   } else {
5826     return NULL;
5827   }
5828 }
5829
5830 static void
5831 _free_set_description_data (struct set_description *sd)
5832 {
5833   if (sd->sdp)
5834     gst_webrtc_session_description_free (sd->sdp);
5835   g_free (sd);
5836 }
5837
5838 static void
5839 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
5840     GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
5841 {
5842   struct set_description *sd;
5843
5844   if (remote_sdp == NULL)
5845     goto bad_input;
5846   if (remote_sdp->sdp == NULL)
5847     goto bad_input;
5848
5849   sd = g_new0 (struct set_description, 1);
5850   sd->source = SDP_REMOTE;
5851   sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
5852
5853   if (!gst_webrtc_bin_enqueue_task (webrtc,
5854           (GstWebRTCBinFunc) _set_description_task, sd,
5855           (GDestroyNotify) _free_set_description_data, promise)) {
5856     GError *error =
5857         g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5858         "Could not set remote description. webrtcbin is closed.");
5859     GstStructure *s = gst_structure_new ("application/x-gst-promise",
5860         "error", G_TYPE_ERROR, error, NULL);
5861
5862     gst_promise_reply (promise, s);
5863
5864     g_clear_error (&error);
5865   }
5866
5867   return;
5868
5869 bad_input:
5870   {
5871     gst_promise_reply (promise, NULL);
5872     g_return_if_reached ();
5873   }
5874 }
5875
5876 static void
5877 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
5878     GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
5879 {
5880   struct set_description *sd;
5881
5882   if (local_sdp == NULL)
5883     goto bad_input;
5884   if (local_sdp->sdp == NULL)
5885     goto bad_input;
5886
5887   sd = g_new0 (struct set_description, 1);
5888   sd->source = SDP_LOCAL;
5889   sd->sdp = gst_webrtc_session_description_copy (local_sdp);
5890
5891   if (!gst_webrtc_bin_enqueue_task (webrtc,
5892           (GstWebRTCBinFunc) _set_description_task, sd,
5893           (GDestroyNotify) _free_set_description_data, promise)) {
5894     GError *error =
5895         g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
5896         "Could not set remote description. webrtcbin is closed");
5897     GstStructure *s = gst_structure_new ("application/x-gst-promise",
5898         "error", G_TYPE_ERROR, error, NULL);
5899
5900     gst_promise_reply (promise, s);
5901
5902     g_clear_error (&error);
5903   }
5904
5905   return;
5906
5907 bad_input:
5908   {
5909     gst_promise_reply (promise, NULL);
5910     g_return_if_reached ();
5911   }
5912 }
5913
5914 static GstStructure *
5915 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
5916 {
5917   if (!webrtc->current_local_description || !webrtc->current_remote_description) {
5918     IceCandidateItem new;
5919     new.mlineindex = item->mlineindex;
5920     new.candidate = g_steal_pointer (&item->candidate);
5921
5922     ICE_LOCK (webrtc);
5923     g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new);
5924     ICE_UNLOCK (webrtc);
5925   } else {
5926     _add_ice_candidate (webrtc, item, FALSE);
5927   }
5928
5929   return NULL;
5930 }
5931
5932 static void
5933 _free_ice_candidate_item (IceCandidateItem * item)
5934 {
5935   _clear_ice_candidate_item (item);
5936   g_free (item);
5937 }
5938
5939 static void
5940 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
5941     const gchar * attr)
5942 {
5943   IceCandidateItem *item;
5944
5945   item = g_new0 (IceCandidateItem, 1);
5946   item->mlineindex = mline;
5947   if (attr && attr[0] != 0) {
5948     if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
5949       item->candidate = g_strdup (attr);
5950     else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
5951       item->candidate = g_strdup_printf ("a=%s", attr);
5952   }
5953   gst_webrtc_bin_enqueue_task (webrtc,
5954       (GstWebRTCBinFunc) _add_ice_candidate_task, item,
5955       (GDestroyNotify) _free_ice_candidate_item, NULL);
5956 }
5957
5958 static GstStructure *
5959 _on_local_ice_candidate_task (GstWebRTCBin * webrtc)
5960 {
5961   gsize i;
5962   GArray *items;
5963
5964   ICE_LOCK (webrtc);
5965   if (webrtc->priv->pending_local_ice_candidates->len == 0) {
5966     ICE_UNLOCK (webrtc);
5967     GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now");
5968     return NULL;                /* Nothing to process */
5969   }
5970   /* Take the array so we can process it all and free it later
5971    * without holding the lock
5972    * FIXME: When we depend on GLib 2.64, we can use g_array_steal()
5973    * here */
5974   items = webrtc->priv->pending_local_ice_candidates;
5975   /* Replace with a new array */
5976   webrtc->priv->pending_local_ice_candidates =
5977       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
5978   g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
5979       (GDestroyNotify) _clear_ice_candidate_item);
5980   ICE_UNLOCK (webrtc);
5981
5982   for (i = 0; i < items->len; i++) {
5983     IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i);
5984     const gchar *cand = item->candidate;
5985
5986     if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
5987       /* stripping away "a=" */
5988       cand += 2;
5989     }
5990
5991     GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
5992         item->mlineindex, cand);
5993
5994     /* First, merge this ice candidate into the appropriate mline
5995      * in the local-description SDP.
5996      * Second, emit the on-ice-candidate signal for the app.
5997      *
5998      * FIXME: This ICE candidate should be stored somewhere with
5999      * the associated mid and also merged back into any subsequent
6000      * local descriptions on renegotiation */
6001     if (webrtc->current_local_description)
6002       _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp,
6003           item->mlineindex, cand);
6004     if (webrtc->pending_local_description)
6005       _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp,
6006           item->mlineindex, cand);
6007
6008     PC_UNLOCK (webrtc);
6009     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
6010         0, item->mlineindex, cand);
6011     PC_LOCK (webrtc);
6012
6013   }
6014   g_array_free (items, TRUE);
6015
6016   return NULL;
6017 }
6018
6019 static void
6020 _on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id,
6021     gchar * candidate, GstWebRTCBin * webrtc)
6022 {
6023   IceCandidateItem item;
6024   gboolean queue_task = FALSE;
6025
6026   item.mlineindex = session_id;
6027   item.candidate = g_strdup (candidate);
6028
6029   ICE_LOCK (webrtc);
6030   g_array_append_val (webrtc->priv->pending_local_ice_candidates, item);
6031
6032   /* Let the first pending candidate queue a task each time, which will
6033    * handle any that arrive between now and when the task runs */
6034   if (webrtc->priv->pending_local_ice_candidates->len == 1)
6035     queue_task = TRUE;
6036   ICE_UNLOCK (webrtc);
6037
6038   if (queue_task) {
6039     GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task");
6040     gst_webrtc_bin_enqueue_task (webrtc,
6041         (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL);
6042   }
6043 }
6044
6045 struct get_stats
6046 {
6047   GstPad *pad;
6048   GstPromise *promise;
6049 };
6050
6051 static void
6052 _free_get_stats (struct get_stats *stats)
6053 {
6054   if (stats->pad)
6055     gst_object_unref (stats->pad);
6056   if (stats->promise)
6057     gst_promise_unref (stats->promise);
6058   g_free (stats);
6059 }
6060
6061 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
6062 static GstStructure *
6063 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
6064 {
6065   /* Our selector is the pad,
6066    * https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm
6067    */
6068
6069   return gst_webrtc_bin_create_stats (webrtc, stats->pad);
6070 }
6071
6072 static void
6073 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
6074     GstPromise * promise)
6075 {
6076   struct get_stats *stats;
6077
6078   g_return_if_fail (promise != NULL);
6079   g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
6080
6081   stats = g_new0 (struct get_stats, 1);
6082   stats->promise = gst_promise_ref (promise);
6083   /* FIXME: check that pad exists in element */
6084   if (pad)
6085     stats->pad = gst_object_ref (pad);
6086
6087   if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
6088           stats, (GDestroyNotify) _free_get_stats, promise)) {
6089     GError *error =
6090         g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED,
6091         "Could not retrieve statistics. webrtcbin is closed.");
6092     GstStructure *s = gst_structure_new ("application/x-gst-promise",
6093         "error", G_TYPE_ERROR, error, NULL);
6094
6095     gst_promise_reply (promise, s);
6096
6097     g_clear_error (&error);
6098   }
6099 }
6100
6101 static GstWebRTCRTPTransceiver *
6102 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
6103     GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
6104 {
6105   WebRTCTransceiver *trans;
6106
6107   g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
6108       NULL);
6109
6110   PC_LOCK (webrtc);
6111
6112   trans =
6113       _create_webrtc_transceiver (webrtc, direction, -1,
6114       webrtc_kind_from_caps (caps), caps);
6115   GST_LOG_OBJECT (webrtc,
6116       "Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
6117
6118   PC_UNLOCK (webrtc);
6119
6120   return gst_object_ref (trans);
6121 }
6122
6123 static void
6124 _deref_and_unref (GstObject ** object)
6125 {
6126   gst_clear_object (object);
6127 }
6128
6129 static GArray *
6130 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
6131 {
6132   GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *));
6133   int i;
6134
6135   PC_LOCK (webrtc);
6136
6137   g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
6138
6139   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6140     GstWebRTCRTPTransceiver *trans =
6141         g_ptr_array_index (webrtc->priv->transceivers, i);
6142     gst_object_ref (trans);
6143     g_array_append_val (arr, trans);
6144   }
6145   PC_UNLOCK (webrtc);
6146
6147   return arr;
6148 }
6149
6150 static GstWebRTCRTPTransceiver *
6151 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
6152 {
6153   GstWebRTCRTPTransceiver *trans = NULL;
6154
6155   PC_LOCK (webrtc);
6156
6157   if (idx >= webrtc->priv->transceivers->len) {
6158     GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
6159     goto done;
6160   }
6161
6162   trans = g_ptr_array_index (webrtc->priv->transceivers, idx);
6163   gst_object_ref (trans);
6164
6165 done:
6166   PC_UNLOCK (webrtc);
6167   return trans;
6168 }
6169
6170 static gboolean
6171 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
6172 {
6173   gboolean ret;
6174
6175   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
6176   g_return_val_if_fail (uri != NULL, FALSE);
6177
6178   GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
6179
6180   PC_LOCK (webrtc);
6181   ret = gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
6182   PC_UNLOCK (webrtc);
6183
6184   return ret;
6185 }
6186
6187 static gboolean
6188 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
6189 {
6190   GstPad *gpad = GST_PAD_CAST (user_data);
6191
6192   GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
6193   gst_pad_store_sticky_event (gpad, *event);
6194
6195   return TRUE;
6196 }
6197
6198 static WebRTCDataChannel *
6199 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
6200     GstStructure * init_params)
6201 {
6202   gboolean ordered;
6203   gint max_packet_lifetime;
6204   gint max_retransmits;
6205   const gchar *protocol;
6206   gboolean negotiated;
6207   gint id;
6208   GstWebRTCPriorityType priority;
6209   WebRTCDataChannel *ret;
6210   gint max_channels = 65534;
6211
6212   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
6213   g_return_val_if_fail (label != NULL, NULL);
6214   g_return_val_if_fail (strlen (label) <= 65535, NULL);
6215   g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
6216
6217   if (!init_params
6218       || !gst_structure_get_boolean (init_params, "ordered", &ordered))
6219     ordered = TRUE;
6220   if (!init_params
6221       || !gst_structure_get_int (init_params, "max-packet-lifetime",
6222           &max_packet_lifetime))
6223     max_packet_lifetime = -1;
6224   if (!init_params
6225       || !gst_structure_get_int (init_params, "max-retransmits",
6226           &max_retransmits))
6227     max_retransmits = -1;
6228   /* both retransmits and lifetime cannot be set */
6229   g_return_val_if_fail ((max_packet_lifetime == -1)
6230       || (max_retransmits == -1), NULL);
6231
6232   if (!init_params
6233       || !(protocol = gst_structure_get_string (init_params, "protocol")))
6234     protocol = "";
6235   g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
6236
6237   if (!init_params
6238       || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
6239     negotiated = FALSE;
6240   if (!negotiated || !init_params
6241       || !gst_structure_get_int (init_params, "id", &id))
6242     id = -1;
6243   if (negotiated)
6244     g_return_val_if_fail (id != -1, NULL);
6245   g_return_val_if_fail (id < 65535, NULL);
6246
6247   if (!init_params
6248       || !gst_structure_get_enum (init_params, "priority",
6249           GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
6250     priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
6251
6252   /* FIXME: clamp max-retransmits and max-packet-lifetime */
6253
6254   if (webrtc->priv->sctp_transport) {
6255     /* Let transport be the connection's [[SctpTransport]] slot.
6256      *
6257      * If the [[DataChannelId]] slot is not null, transport is in
6258      * connected state and [[DataChannelId]] is greater or equal to the
6259      * transport's [[MaxChannels]] slot, throw an OperationError.
6260      */
6261     g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
6262         NULL);
6263
6264     g_return_val_if_fail (id <= max_channels, NULL);
6265   }
6266
6267   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
6268       !_have_sctp_elements (webrtc))
6269     return NULL;
6270
6271   PC_LOCK (webrtc);
6272   DC_LOCK (webrtc);
6273   /* check if the id has been used already */
6274   if (id != -1) {
6275     WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
6276     if (channel) {
6277       GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
6278           ("Attempting to add a data channel with a duplicate ID: %i", id),
6279           NULL);
6280       DC_UNLOCK (webrtc);
6281       PC_UNLOCK (webrtc);
6282       return NULL;
6283     }
6284   } else if (webrtc->current_local_description
6285       && webrtc->current_remote_description && webrtc->priv->sctp_transport
6286       && webrtc->priv->sctp_transport->transport) {
6287     /* else we can only generate an id if we're configured already.  The other
6288      * case for generating an id is on sdp setting */
6289     id = _generate_data_channel_id (webrtc);
6290     if (id == -1) {
6291       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
6292           ("%s", "Failed to generate an identifier for a data channel"), NULL);
6293       DC_UNLOCK (webrtc);
6294       PC_UNLOCK (webrtc);
6295       return NULL;
6296     }
6297   }
6298
6299   ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
6300       "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
6301       "max-retransmits", max_retransmits, "protocol", protocol,
6302       "negotiated", negotiated, "id", id, "priority", priority, NULL);
6303
6304   if (!ret) {
6305     DC_UNLOCK (webrtc);
6306     PC_UNLOCK (webrtc);
6307     return ret;
6308   }
6309
6310   gst_bin_add (GST_BIN (webrtc), ret->appsrc);
6311   gst_bin_add (GST_BIN (webrtc), ret->appsink);
6312
6313   gst_element_sync_state_with_parent (ret->appsrc);
6314   gst_element_sync_state_with_parent (ret->appsink);
6315
6316   ret = gst_object_ref (ret);
6317   ret->webrtcbin = webrtc;
6318   g_ptr_array_add (webrtc->priv->data_channels, ret);
6319   DC_UNLOCK (webrtc);
6320
6321   gst_webrtc_bin_update_sctp_priority (webrtc);
6322   webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
6323   if (webrtc->priv->sctp_transport &&
6324       webrtc->priv->sctp_transport->association_established
6325       && !ret->parent.negotiated) {
6326     webrtc_data_channel_start_negotiation (ret);
6327   } else {
6328     _update_need_negotiation (webrtc);
6329   }
6330
6331   PC_UNLOCK (webrtc);
6332   return ret;
6333 }
6334
6335 /* === rtpbin signal implementations === */
6336
6337 static void
6338 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
6339     GstWebRTCBin * webrtc)
6340 {
6341   gchar *new_pad_name = NULL;
6342
6343   new_pad_name = gst_pad_get_name (new_pad);
6344   GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
6345   if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
6346     guint32 session_id = 0, ssrc = 0, pt = 0;
6347     GstWebRTCRTPTransceiver *rtp_trans;
6348     WebRTCTransceiver *trans;
6349     TransportStream *stream;
6350     GstWebRTCBinPad *pad;
6351     guint media_idx = 0;
6352     gboolean found_ssrc = FALSE;
6353     guint i;
6354
6355     if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
6356             &pt) != 3) {
6357       g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
6358       return;
6359     }
6360
6361     stream = _find_transport_for_session (webrtc, session_id);
6362     if (!stream)
6363       g_warn_if_reached ();
6364
6365     media_idx = session_id;
6366
6367     for (i = 0; i < stream->remote_ssrcmap->len; i++) {
6368       SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
6369       if (item->ssrc == ssrc) {
6370         media_idx = item->media_idx;
6371         found_ssrc = TRUE;
6372         break;
6373       }
6374     }
6375
6376     if (!found_ssrc) {
6377       GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
6378     }
6379
6380     rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
6381     if (!rtp_trans)
6382       g_warn_if_reached ();
6383     trans = WEBRTC_TRANSCEIVER (rtp_trans);
6384     g_assert (trans->stream == stream);
6385
6386     pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
6387
6388     GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
6389         " for rtpbin pad name %s", pad, new_pad_name);
6390     if (!pad)
6391       g_warn_if_reached ();
6392     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
6393
6394     if (webrtc->priv->running)
6395       gst_pad_set_active (GST_PAD (pad), TRUE);
6396     gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
6397     gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
6398     _remove_pending_pad (webrtc, pad);
6399
6400     gst_object_unref (pad);
6401   }
6402   g_free (new_pad_name);
6403 }
6404
6405 /* only used for the receiving streams */
6406 static GstCaps *
6407 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
6408     GstWebRTCBin * webrtc)
6409 {
6410   TransportStream *stream;
6411   GstCaps *ret;
6412
6413   GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
6414       session_id);
6415
6416   stream = _find_transport_for_session (webrtc, session_id);
6417   if (!stream)
6418     goto unknown_session;
6419
6420   if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
6421     gst_caps_ref (ret);
6422
6423   GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
6424       "session %d", ret, pt, session_id);
6425
6426   return ret;
6427
6428 unknown_session:
6429   {
6430     GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
6431     return NULL;
6432   }
6433 }
6434
6435 static gboolean
6436 _merge_structure (GQuark field_id, const GValue * value, gpointer user_data)
6437 {
6438   GstStructure *s = user_data;
6439
6440   gst_structure_id_set_value (s, field_id, value);
6441
6442   return TRUE;
6443 }
6444
6445 static GstElement *
6446 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
6447     GstWebRTCBin * webrtc)
6448 {
6449   TransportStream *stream;
6450   gboolean have_rtx = FALSE;
6451   GstStructure *pt_map = NULL;
6452   GstElement *ret = NULL;
6453
6454   stream = _find_transport_for_session (webrtc, session_id);
6455
6456   if (stream)
6457     have_rtx = transport_stream_get_pt (stream, "RTX", -1) != 0;
6458
6459   GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
6460       " with pt map %" GST_PTR_FORMAT, stream, pt_map);
6461
6462   if (have_rtx) {
6463     GstElement *rtx;
6464     GstPad *pad;
6465     gchar *name;
6466     GstStructure *merged_local_rtx_ssrc_map =
6467         gst_structure_new_empty ("application/x-rtp-ssrc-map");
6468     guint i;
6469
6470     if (stream->rtxsend) {
6471       GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
6472       goto out;
6473     }
6474
6475     GST_INFO ("creating AUX sender");
6476     ret = gst_bin_new (NULL);
6477     rtx = gst_element_factory_make ("rtprtxsend", NULL);
6478     g_object_set (rtx, "max-size-packets", 500, NULL);
6479     _set_rtx_ptmap_from_stream (webrtc, stream);
6480
6481     for (i = 0; i < webrtc->priv->transceivers->len; i++) {
6482       WebRTCTransceiver *trans =
6483           WEBRTC_TRANSCEIVER (g_ptr_array_index (webrtc->priv->transceivers,
6484               i));
6485
6486       if (trans->stream == stream && trans->local_rtx_ssrc_map)
6487         gst_structure_foreach (trans->local_rtx_ssrc_map,
6488             _merge_structure, merged_local_rtx_ssrc_map);
6489     }
6490
6491     g_object_set (rtx, "ssrc-map", merged_local_rtx_ssrc_map, NULL);
6492     gst_structure_free (merged_local_rtx_ssrc_map);
6493
6494     gst_bin_add (GST_BIN (ret), rtx);
6495
6496     pad = gst_element_get_static_pad (rtx, "src");
6497     name = g_strdup_printf ("src_%u", session_id);
6498     gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
6499     g_free (name);
6500     gst_object_unref (pad);
6501
6502     pad = gst_element_get_static_pad (rtx, "sink");
6503     name = g_strdup_printf ("sink_%u", session_id);
6504     gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
6505     g_free (name);
6506     gst_object_unref (pad);
6507
6508     stream->rtxsend = gst_object_ref (rtx);
6509   }
6510
6511 out:
6512   if (pt_map)
6513     gst_structure_free (pt_map);
6514
6515   return ret;
6516 }
6517
6518 static GstElement *
6519 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
6520     GstWebRTCBin * webrtc)
6521 {
6522   GstElement *ret = NULL;
6523   GstElement *prev = NULL;
6524   GstPad *sinkpad = NULL;
6525   TransportStream *stream;
6526   gint rtx_pt = 0;
6527   GValue red_pt_array = { 0, };
6528   gboolean have_red_pt = FALSE;
6529
6530   g_value_init (&red_pt_array, GST_TYPE_ARRAY);
6531
6532   stream = _find_transport_for_session (webrtc, session_id);
6533
6534   if (stream) {
6535     guint i = 0;
6536
6537     for (i = 0; i < stream->ptmap->len; i++) {
6538       PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
6539
6540       if (!gst_caps_is_empty (item->caps)) {
6541         GstStructure *s = gst_caps_get_structure (item->caps, 0);
6542
6543         if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "RED")) {
6544           GValue ptval = { 0, };
6545
6546           g_value_init (&ptval, G_TYPE_INT);
6547           g_value_set_int (&ptval, item->pt);
6548           gst_value_array_append_value (&red_pt_array, &ptval);
6549           g_value_unset (&ptval);
6550
6551           have_red_pt = TRUE;
6552         }
6553       }
6554     }
6555
6556     rtx_pt = transport_stream_get_pt (stream, "RTX", -1);
6557   }
6558
6559   GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
6560       stream);
6561
6562   if (have_red_pt || rtx_pt)
6563     ret = gst_bin_new (NULL);
6564
6565   if (rtx_pt) {
6566     if (stream->rtxreceive) {
6567       GST_WARNING_OBJECT (webrtc,
6568           "rtprtxreceive already created! rtpbin bug?!");
6569       goto error;
6570     }
6571
6572     stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
6573     _set_rtx_ptmap_from_stream (webrtc, stream);
6574
6575     gst_bin_add (GST_BIN (ret), stream->rtxreceive);
6576
6577     sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
6578
6579     prev = gst_object_ref (stream->rtxreceive);
6580   }
6581
6582   if (have_red_pt) {
6583     GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
6584
6585     GST_DEBUG_OBJECT (webrtc, "Creating RED decoder in session %u", session_id);
6586
6587     gst_bin_add (GST_BIN (ret), rtpreddec);
6588
6589     g_object_set_property (G_OBJECT (rtpreddec), "payloads", &red_pt_array);
6590
6591     if (prev)
6592       gst_element_link (prev, rtpreddec);
6593     else
6594       sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
6595
6596     prev = rtpreddec;
6597   }
6598
6599   if (sinkpad) {
6600     gchar *name = g_strdup_printf ("sink_%u", session_id);
6601     GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
6602     g_free (name);
6603     gst_object_unref (sinkpad);
6604     gst_element_add_pad (ret, ghost);
6605   }
6606
6607   if (prev) {
6608     gchar *name = g_strdup_printf ("src_%u", session_id);
6609     GstPad *srcpad = gst_element_get_static_pad (prev, "src");
6610     GstPad *ghost = gst_ghost_pad_new (name, srcpad);
6611     g_free (name);
6612     gst_object_unref (srcpad);
6613     gst_element_add_pad (ret, ghost);
6614   }
6615
6616 out:
6617   g_value_unset (&red_pt_array);
6618   return ret;
6619
6620 error:
6621   if (ret)
6622     gst_object_unref (ret);
6623   goto out;
6624 }
6625
6626 static GstElement *
6627 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
6628     guint ssrc, guint pt, GstWebRTCBin * webrtc)
6629 {
6630   TransportStream *stream;
6631   GstElement *ret = NULL;
6632   gint fec_pt = 0;
6633   GObject *internal_storage;
6634
6635   stream = _find_transport_for_session (webrtc, session_id);
6636
6637   /* TODO: for now, we only support ulpfec, but once we support
6638    * more algorithms, if the remote may use more than one algorithm,
6639    * we will want to do the following:
6640    *
6641    * + Return a bin here, with the relevant FEC decoders plugged in
6642    *   and their payload type set to 0
6643    * + Enable the decoders by setting the payload type only when
6644    *   we detect it (by connecting to ptdemux:new-payload-type for
6645    *   example)
6646    */
6647   if (stream) {
6648     guint i;
6649
6650     for (i = 0; i < stream->ptmap->len; i++) {
6651       PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
6652
6653       if (item->pt == pt) {
6654         fec_pt = transport_stream_get_pt (stream, "ULPFEC", item->media_idx);
6655         break;
6656       }
6657     }
6658   }
6659
6660   if (fec_pt) {
6661     GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
6662         fec_pt, session_id);
6663     ret = gst_element_factory_make ("rtpulpfecdec", NULL);
6664     g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
6665         &internal_storage);
6666
6667     g_object_set (ret, "pt", fec_pt, "storage", internal_storage, NULL);
6668     g_object_unref (internal_storage);
6669   }
6670
6671   return ret;
6672 }
6673
6674 static void
6675 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6676     GstWebRTCBin * webrtc)
6677 {
6678   GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
6679 }
6680
6681 static void
6682 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6683     GstWebRTCBin * webrtc)
6684 {
6685   GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
6686 }
6687
6688 static void
6689 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6690     GstWebRTCBin * webrtc)
6691 {
6692   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
6693       ssrc);
6694 }
6695
6696 static void
6697 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6698     GstWebRTCBin * webrtc)
6699 {
6700   GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
6701 }
6702
6703 static void
6704 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6705     GstWebRTCBin * webrtc)
6706 {
6707   GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
6708 }
6709
6710 static void
6711 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
6712     GstWebRTCBin * webrtc)
6713 {
6714   GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
6715 }
6716
6717 static void
6718 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
6719     GstWebRTCBin * webrtc)
6720 {
6721   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
6722 }
6723
6724 static void
6725 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
6726     GstWebRTCBin * webrtc)
6727 {
6728   GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
6729 }
6730
6731 static void
6732 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
6733     GstWebRTCBin * webrtc)
6734 {
6735   GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
6736 }
6737
6738 static void
6739 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
6740     GstWebRTCBin * webrtc)
6741 {
6742   GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
6743       ssrc);
6744 }
6745
6746 static void
6747 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
6748     GstWebRTCBin * webrtc)
6749 {
6750   GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
6751       ssrc);
6752 }
6753
6754 static void
6755 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
6756     guint session_id, guint ssrc, GstWebRTCBin * webrtc)
6757 {
6758   TransportStream *stream;
6759   guint i;
6760
6761   PC_LOCK (webrtc);
6762   GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
6763       "session %u ssrc %u", jitterbuffer, session_id, ssrc);
6764
6765   if (!(stream = _find_transport_for_session (webrtc, session_id))) {
6766     g_warn_if_reached ();
6767     goto out;
6768   }
6769
6770   /* XXX: this will fail with no ssrc in the remote sdp as used with e.g. simulcast
6771    * newer SDP versions from chrome/firefox */
6772   for (i = 0; i < stream->remote_ssrcmap->len; i++) {
6773     SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
6774
6775     if (item->ssrc == ssrc) {
6776       GstWebRTCRTPTransceiver *trans;
6777       gboolean do_nack;
6778
6779       trans = _find_transceiver_for_mline (webrtc, item->media_idx);
6780       if (!trans) {
6781         g_warn_if_reached ();
6782         break;
6783       }
6784
6785       do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
6786       /* We don't set do-retransmission on rtpbin as we want per-session control */
6787       GST_LOG_OBJECT (webrtc, "setting do-nack=%s for transceiver %"
6788           GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
6789           " rtp session %u ssrc %u", do_nack ? "true" : "false", trans, stream,
6790           session_id, ssrc);
6791       g_object_set (jitterbuffer, "do-retransmission", do_nack, NULL);
6792
6793       g_weak_ref_set (&item->rtpjitterbuffer, jitterbuffer);
6794       break;
6795     }
6796   }
6797 out:
6798   PC_UNLOCK (webrtc);
6799 }
6800
6801 static void
6802 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
6803     guint session_id, GstWebRTCBin * webrtc)
6804 {
6805   guint64 latency = webrtc->priv->jb_latency;
6806
6807   /* Add an extra 50 ms for safey */
6808   latency += RTPSTORAGE_EXTRA_TIME;
6809   latency *= GST_MSECOND;
6810
6811   g_object_set (storage, "size-time", latency, NULL);
6812 }
6813
6814 static GstElement *
6815 _create_rtpbin (GstWebRTCBin * webrtc)
6816 {
6817   GstElement *rtpbin;
6818
6819   if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
6820     return NULL;
6821
6822   /* mandated by WebRTC */
6823   gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
6824
6825   g_object_set (rtpbin, "do-lost", TRUE, NULL);
6826
6827   g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
6828       webrtc);
6829   g_signal_connect (rtpbin, "request-pt-map",
6830       G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
6831   g_signal_connect (rtpbin, "request-aux-sender",
6832       G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
6833   g_signal_connect (rtpbin, "request-aux-receiver",
6834       G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
6835   g_signal_connect (rtpbin, "new-storage",
6836       G_CALLBACK (on_rtpbin_new_storage), webrtc);
6837   g_signal_connect (rtpbin, "request-fec-decoder-full",
6838       G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
6839   g_signal_connect (rtpbin, "on-bye-ssrc",
6840       G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
6841   g_signal_connect (rtpbin, "on-bye-timeout",
6842       G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
6843   g_signal_connect (rtpbin, "on-new-ssrc",
6844       G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
6845   g_signal_connect (rtpbin, "on-new-sender-ssrc",
6846       G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
6847   g_signal_connect (rtpbin, "on-sender-ssrc-active",
6848       G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
6849   g_signal_connect (rtpbin, "on-sender-timeout",
6850       G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
6851   g_signal_connect (rtpbin, "on-ssrc-active",
6852       G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
6853   g_signal_connect (rtpbin, "on-ssrc-collision",
6854       G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
6855   g_signal_connect (rtpbin, "on-ssrc-sdes",
6856       G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
6857   g_signal_connect (rtpbin, "on-ssrc-validated",
6858       G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
6859   g_signal_connect (rtpbin, "on-timeout",
6860       G_CALLBACK (on_rtpbin_timeout), webrtc);
6861   g_signal_connect (rtpbin, "new-jitterbuffer",
6862       G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
6863
6864   return rtpbin;
6865 }
6866
6867 static GstStateChangeReturn
6868 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
6869 {
6870   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6871   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
6872
6873   GST_DEBUG ("changing state: %s => %s",
6874       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
6875       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
6876
6877   switch (transition) {
6878     case GST_STATE_CHANGE_NULL_TO_READY:{
6879       if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6880         return GST_STATE_CHANGE_FAILURE;
6881       _start_thread (webrtc);
6882       PC_LOCK (webrtc);
6883       _update_need_negotiation (webrtc);
6884       PC_UNLOCK (webrtc);
6885       break;
6886     }
6887     case GST_STATE_CHANGE_READY_TO_PAUSED:
6888       webrtc->priv->running = TRUE;
6889       break;
6890     default:
6891       break;
6892   }
6893
6894   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6895   if (ret == GST_STATE_CHANGE_FAILURE)
6896     return ret;
6897
6898   switch (transition) {
6899     case GST_STATE_CHANGE_READY_TO_PAUSED:
6900       /* Mangle the return value to NO_PREROLL as that's what really is
6901        * occurring here however cannot be propagated correctly due to nicesrc
6902        * requiring that it be in PLAYING already in order to send/receive
6903        * correctly :/ */
6904       ret = GST_STATE_CHANGE_NO_PREROLL;
6905       break;
6906     case GST_STATE_CHANGE_PAUSED_TO_READY:
6907       webrtc->priv->running = FALSE;
6908       break;
6909     case GST_STATE_CHANGE_READY_TO_NULL:
6910       _stop_thread (webrtc);
6911       break;
6912     default:
6913       break;
6914   }
6915
6916   return ret;
6917 }
6918
6919 static GstPadProbeReturn
6920 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
6921 {
6922   GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
6923
6924   return GST_PAD_PROBE_OK;
6925 }
6926
6927
6928 static GstPad *
6929 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
6930     const gchar * name, const GstCaps * caps)
6931 {
6932   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
6933   GstWebRTCRTPTransceiver *trans = NULL;
6934   GstWebRTCBinPad *pad = NULL;
6935   guint serial;
6936   gboolean lock_mline = FALSE;
6937
6938   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
6939     return NULL;
6940
6941   if (templ->direction != GST_PAD_SINK ||
6942       g_strcmp0 (templ->name_template, "sink_%u") != 0) {
6943     GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
6944     return NULL;
6945   }
6946
6947   PC_LOCK (webrtc);
6948
6949   if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
6950     /* no name given when requesting the pad, use next available int */
6951     serial = webrtc->priv->max_sink_pad_serial++;
6952   } else {
6953     /* parse serial number from requested padname */
6954     serial = g_ascii_strtoull (&name[5], NULL, 10);
6955     lock_mline = TRUE;
6956   }
6957
6958   if (lock_mline) {
6959     GstWebRTCBinPad *pad2;
6960
6961     trans = _find_transceiver_for_mline (webrtc, serial);
6962
6963     if (trans) {
6964       /* Reject transceivers that are only for receiving ... */
6965       if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
6966           trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
6967         gchar *direction =
6968             g_enum_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
6969             trans->direction);
6970         GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6971             " existing m-line %d, but the transceiver's direction is %s",
6972             name, serial, direction);
6973         g_free (direction);
6974         goto error_out;
6975       }
6976
6977       /* Reject transceivers that already have a pad allocated */
6978       pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
6979       if (pad2) {
6980         GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
6981             " but the transceiver associated with this m-line already has pad"
6982             " %s", name, serial, GST_PAD_NAME (pad2));
6983         gst_object_unref (pad2);
6984         goto error_out;
6985       }
6986
6987       if (caps) {
6988         GST_OBJECT_LOCK (trans);
6989         if (trans->codec_preferences &&
6990             !gst_caps_can_intersect (caps, trans->codec_preferences)) {
6991           GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
6992               " existing m-line %d, but requested caps %" GST_PTR_FORMAT
6993               " don't match existing codec preferences %" GST_PTR_FORMAT,
6994               name, serial, caps, trans->codec_preferences);
6995           GST_OBJECT_UNLOCK (trans);
6996           goto error_out;
6997         }
6998         GST_OBJECT_UNLOCK (trans);
6999
7000         if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
7001           GstWebRTCKind kind = webrtc_kind_from_caps (caps);
7002
7003           if (trans->kind != kind) {
7004             GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
7005                 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
7006                 " don't match transceiver kind %d",
7007                 name, serial, caps, trans->kind);
7008             goto error_out;
7009           }
7010         }
7011       }
7012     }
7013   }
7014
7015   /* Let's try to find a free transceiver that matches */
7016   if (!trans) {
7017     GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
7018     guint i;
7019
7020     kind = webrtc_kind_from_caps (caps);
7021
7022     for (i = 0; i < webrtc->priv->transceivers->len; i++) {
7023       GstWebRTCRTPTransceiver *tmptrans =
7024           g_ptr_array_index (webrtc->priv->transceivers, i);
7025       GstWebRTCBinPad *pad2;
7026       gboolean has_matching_caps;
7027
7028       /* Ignore transceivers with a non-matching kind */
7029       if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
7030           kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
7031         continue;
7032
7033       /* Ignore stopped transmitters */
7034       if (tmptrans->stopped)
7035         continue;
7036
7037       /* Ignore transceivers that are only for receiving ... */
7038       if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
7039           || tmptrans->direction ==
7040           GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
7041         continue;
7042
7043       /* Ignore transceivers that already have a pad allocated */
7044       pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
7045       if (pad2) {
7046         gst_object_unref (pad2);
7047         continue;
7048       }
7049
7050       GST_OBJECT_LOCK (tmptrans);
7051       has_matching_caps = (caps && tmptrans->codec_preferences &&
7052           !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
7053       GST_OBJECT_UNLOCK (tmptrans);
7054       /* Ignore transceivers with non-matching caps */
7055       if (!has_matching_caps)
7056         continue;
7057
7058       trans = tmptrans;
7059       break;
7060     }
7061   }
7062
7063   if (!trans) {
7064     trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
7065             GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
7066             webrtc_kind_from_caps (caps), NULL));
7067     GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
7068   } else {
7069     GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
7070         " for mline %u", trans, serial);
7071     if (caps) {
7072       if (!_update_transceiver_kind_from_caps (trans, caps))
7073         GST_WARNING_OBJECT (webrtc,
7074             "Trying to change transceiver %d kind from %d to %d",
7075             serial, trans->kind, webrtc_kind_from_caps (caps));
7076     }
7077   }
7078   pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial);
7079
7080   pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
7081       GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
7082       (GstPadProbeCallback) sink_pad_block, NULL, NULL);
7083   webrtc->priv->pending_sink_transceivers =
7084       g_list_append (webrtc->priv->pending_sink_transceivers,
7085       gst_object_ref (pad));
7086
7087   if (lock_mline) {
7088     WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
7089     wtrans->mline_locked = TRUE;
7090     trans->mline = serial;
7091   }
7092
7093   PC_UNLOCK (webrtc);
7094
7095   _add_pad (webrtc, pad);
7096
7097   return GST_PAD (pad);
7098
7099 error_out:
7100   PC_UNLOCK (webrtc);
7101   return NULL;
7102 }
7103
7104 static void
7105 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
7106 {
7107   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7108   GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7109
7110   GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
7111
7112   /* remove the transceiver from the pad so that subsequent code doesn't use
7113    * a possibly dead transceiver */
7114   PC_LOCK (webrtc);
7115   if (webrtc_pad->trans)
7116     gst_object_unref (webrtc_pad->trans);
7117   webrtc_pad->trans = NULL;
7118   gst_caps_replace (&webrtc_pad->received_caps, NULL);
7119   PC_UNLOCK (webrtc);
7120
7121   _remove_pad (webrtc, webrtc_pad);
7122
7123   PC_LOCK (webrtc);
7124   _update_need_negotiation (webrtc);
7125   PC_UNLOCK (webrtc);
7126 }
7127
7128 static void
7129 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
7130 {
7131   guint i;
7132   guint64 latency_ns;
7133
7134   /* Add an extra 50 ms for safety */
7135   latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
7136   latency_ns *= GST_MSECOND;
7137
7138   for (i = 0; i < webrtc->priv->transports->len; i++) {
7139     TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
7140     GObject *storage = NULL;
7141
7142     g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
7143         &storage);
7144
7145     g_object_set (storage, "size-time", latency_ns, NULL);
7146
7147     g_object_unref (storage);
7148   }
7149 }
7150
7151 static void
7152 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
7153     const GValue * value, GParamSpec * pspec)
7154 {
7155   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7156
7157   switch (prop_id) {
7158     case PROP_STUN_SERVER:
7159       gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
7160           g_value_get_string (value));
7161       break;
7162     case PROP_TURN_SERVER:
7163       gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
7164           g_value_get_string (value));
7165       break;
7166     case PROP_BUNDLE_POLICY:
7167       if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
7168         GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
7169       } else {
7170         webrtc->bundle_policy = g_value_get_enum (value);
7171       }
7172       break;
7173     case PROP_ICE_TRANSPORT_POLICY:
7174       webrtc->ice_transport_policy = g_value_get_enum (value);
7175       gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
7176           webrtc->ice_transport_policy ==
7177           GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
7178       break;
7179     case PROP_LATENCY:
7180       g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
7181       webrtc->priv->jb_latency = g_value_get_uint (value);
7182       _update_rtpstorage_latency (webrtc);
7183       break;
7184     default:
7185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7186       break;
7187   }
7188 }
7189
7190 static void
7191 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
7192     GValue * value, GParamSpec * pspec)
7193 {
7194   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7195
7196   PC_LOCK (webrtc);
7197   switch (prop_id) {
7198     case PROP_CONNECTION_STATE:
7199       g_value_set_enum (value, webrtc->peer_connection_state);
7200       break;
7201     case PROP_SIGNALING_STATE:
7202       g_value_set_enum (value, webrtc->signaling_state);
7203       break;
7204     case PROP_ICE_GATHERING_STATE:
7205       g_value_set_enum (value, webrtc->ice_gathering_state);
7206       break;
7207     case PROP_ICE_CONNECTION_STATE:
7208       g_value_set_enum (value, webrtc->ice_connection_state);
7209       break;
7210     case PROP_LOCAL_DESCRIPTION:
7211       if (webrtc->pending_local_description)
7212         g_value_set_boxed (value, webrtc->pending_local_description);
7213       else if (webrtc->current_local_description)
7214         g_value_set_boxed (value, webrtc->current_local_description);
7215       else
7216         g_value_set_boxed (value, NULL);
7217       break;
7218     case PROP_CURRENT_LOCAL_DESCRIPTION:
7219       g_value_set_boxed (value, webrtc->current_local_description);
7220       break;
7221     case PROP_PENDING_LOCAL_DESCRIPTION:
7222       g_value_set_boxed (value, webrtc->pending_local_description);
7223       break;
7224     case PROP_REMOTE_DESCRIPTION:
7225       if (webrtc->pending_remote_description)
7226         g_value_set_boxed (value, webrtc->pending_remote_description);
7227       else if (webrtc->current_remote_description)
7228         g_value_set_boxed (value, webrtc->current_remote_description);
7229       else
7230         g_value_set_boxed (value, NULL);
7231       break;
7232     case PROP_CURRENT_REMOTE_DESCRIPTION:
7233       g_value_set_boxed (value, webrtc->current_remote_description);
7234       break;
7235     case PROP_PENDING_REMOTE_DESCRIPTION:
7236       g_value_set_boxed (value, webrtc->pending_remote_description);
7237       break;
7238     case PROP_STUN_SERVER:
7239       g_value_take_string (value,
7240           gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
7241       break;
7242     case PROP_TURN_SERVER:
7243       g_value_take_string (value,
7244           gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
7245       break;
7246     case PROP_BUNDLE_POLICY:
7247       g_value_set_enum (value, webrtc->bundle_policy);
7248       break;
7249     case PROP_ICE_TRANSPORT_POLICY:
7250       g_value_set_enum (value, webrtc->ice_transport_policy);
7251       break;
7252     case PROP_ICE_AGENT:
7253       g_value_set_object (value, webrtc->priv->ice);
7254       break;
7255     case PROP_LATENCY:
7256       g_value_set_uint (value, webrtc->priv->jb_latency);
7257       break;
7258     case PROP_SCTP_TRANSPORT:
7259       g_value_set_object (value, webrtc->priv->sctp_transport);
7260       break;
7261     default:
7262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
7263       break;
7264   }
7265   PC_UNLOCK (webrtc);
7266 }
7267
7268 static void
7269 gst_webrtc_bin_constructed (GObject * object)
7270 {
7271   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7272   gchar *name;
7273
7274   name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
7275   webrtc->priv->ice = gst_webrtc_ice_new (name);
7276
7277   gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
7278       (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
7279
7280   g_free (name);
7281 }
7282
7283 static void
7284 _free_pending_pad (GstPad * pad)
7285 {
7286   gst_object_unref (pad);
7287 }
7288
7289 static void
7290 gst_webrtc_bin_dispose (GObject * object)
7291 {
7292   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7293
7294   if (webrtc->priv->ice)
7295     gst_object_unref (webrtc->priv->ice);
7296   webrtc->priv->ice = NULL;
7297
7298   if (webrtc->priv->ice_stream_map)
7299     g_array_free (webrtc->priv->ice_stream_map, TRUE);
7300   webrtc->priv->ice_stream_map = NULL;
7301
7302   g_clear_object (&webrtc->priv->sctp_transport);
7303
7304   G_OBJECT_CLASS (parent_class)->dispose (object);
7305 }
7306
7307 static void
7308 gst_webrtc_bin_finalize (GObject * object)
7309 {
7310   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
7311
7312   if (webrtc->priv->transports)
7313     g_ptr_array_free (webrtc->priv->transports, TRUE);
7314   webrtc->priv->transports = NULL;
7315
7316   if (webrtc->priv->transceivers)
7317     g_ptr_array_free (webrtc->priv->transceivers, TRUE);
7318   webrtc->priv->transceivers = NULL;
7319
7320   if (webrtc->priv->data_channels)
7321     g_ptr_array_free (webrtc->priv->data_channels, TRUE);
7322   webrtc->priv->data_channels = NULL;
7323
7324   if (webrtc->priv->pending_data_channels)
7325     g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
7326   webrtc->priv->pending_data_channels = NULL;
7327
7328   if (webrtc->priv->pending_remote_ice_candidates)
7329     g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
7330   webrtc->priv->pending_remote_ice_candidates = NULL;
7331
7332   if (webrtc->priv->pending_local_ice_candidates)
7333     g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
7334   webrtc->priv->pending_local_ice_candidates = NULL;
7335
7336   if (webrtc->priv->pending_pads)
7337     g_list_free_full (webrtc->priv->pending_pads,
7338         (GDestroyNotify) _free_pending_pad);
7339   webrtc->priv->pending_pads = NULL;
7340
7341   if (webrtc->priv->pending_sink_transceivers)
7342     g_list_free_full (webrtc->priv->pending_sink_transceivers,
7343         (GDestroyNotify) gst_object_unref);
7344   webrtc->priv->pending_sink_transceivers = NULL;
7345
7346   if (webrtc->current_local_description)
7347     gst_webrtc_session_description_free (webrtc->current_local_description);
7348   webrtc->current_local_description = NULL;
7349   if (webrtc->pending_local_description)
7350     gst_webrtc_session_description_free (webrtc->pending_local_description);
7351   webrtc->pending_local_description = NULL;
7352
7353   if (webrtc->current_remote_description)
7354     gst_webrtc_session_description_free (webrtc->current_remote_description);
7355   webrtc->current_remote_description = NULL;
7356   if (webrtc->pending_remote_description)
7357     gst_webrtc_session_description_free (webrtc->pending_remote_description);
7358   webrtc->pending_remote_description = NULL;
7359
7360   if (webrtc->priv->last_generated_answer)
7361     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
7362   webrtc->priv->last_generated_answer = NULL;
7363   if (webrtc->priv->last_generated_offer)
7364     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
7365   webrtc->priv->last_generated_offer = NULL;
7366
7367   g_mutex_clear (DC_GET_LOCK (webrtc));
7368   g_mutex_clear (ICE_GET_LOCK (webrtc));
7369   g_mutex_clear (PC_GET_LOCK (webrtc));
7370   g_cond_clear (PC_GET_COND (webrtc));
7371
7372   G_OBJECT_CLASS (parent_class)->finalize (object);
7373 }
7374
7375 static void
7376 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
7377 {
7378   GObjectClass *gobject_class = (GObjectClass *) klass;
7379   GstElementClass *element_class = (GstElementClass *) klass;
7380
7381   element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
7382   element_class->release_pad = gst_webrtc_bin_release_pad;
7383   element_class->change_state = gst_webrtc_bin_change_state;
7384
7385   gst_element_class_add_static_pad_template_with_gtype (element_class,
7386       &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
7387   gst_element_class_add_static_pad_template (element_class, &src_template);
7388
7389   gst_element_class_set_metadata (element_class, "WebRTC Bin",
7390       "Filter/Network/WebRTC", "A bin for webrtc connections",
7391       "Matthew Waters <matthew@centricular.com>");
7392
7393   gobject_class->constructed = gst_webrtc_bin_constructed;
7394   gobject_class->get_property = gst_webrtc_bin_get_property;
7395   gobject_class->set_property = gst_webrtc_bin_set_property;
7396   gobject_class->dispose = gst_webrtc_bin_dispose;
7397   gobject_class->finalize = gst_webrtc_bin_finalize;
7398
7399   g_object_class_install_property (gobject_class,
7400       PROP_LOCAL_DESCRIPTION,
7401       g_param_spec_boxed ("local-description", "Local Description",
7402           "The local SDP description in use for this connection. "
7403           "Favours a pending description over the current description",
7404           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7405           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7406
7407   g_object_class_install_property (gobject_class,
7408       PROP_CURRENT_LOCAL_DESCRIPTION,
7409       g_param_spec_boxed ("current-local-description",
7410           "Current Local Description",
7411           "The local description that was successfully negotiated the last time "
7412           "the connection transitioned into the stable state",
7413           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7414           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7415
7416   g_object_class_install_property (gobject_class,
7417       PROP_PENDING_LOCAL_DESCRIPTION,
7418       g_param_spec_boxed ("pending-local-description",
7419           "Pending Local Description",
7420           "The local description that is in the process of being negotiated plus "
7421           "any local candidates that have been generated by the ICE Agent since the "
7422           "offer or answer was created",
7423           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7424           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7425
7426   g_object_class_install_property (gobject_class,
7427       PROP_REMOTE_DESCRIPTION,
7428       g_param_spec_boxed ("remote-description", "Remote Description",
7429           "The remote SDP description to use for this connection. "
7430           "Favours a pending description over the current description",
7431           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7432           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7433
7434   g_object_class_install_property (gobject_class,
7435       PROP_CURRENT_REMOTE_DESCRIPTION,
7436       g_param_spec_boxed ("current-remote-description",
7437           "Current Remote Description",
7438           "The last remote description that was successfully negotiated the last "
7439           "time the connection transitioned into the stable state plus any remote "
7440           "candidates that have been supplied via addIceCandidate() since the offer "
7441           "or answer was created",
7442           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7443           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7444
7445   g_object_class_install_property (gobject_class,
7446       PROP_PENDING_REMOTE_DESCRIPTION,
7447       g_param_spec_boxed ("pending-remote-description",
7448           "Pending Remote Description",
7449           "The remote description that is in the process of being negotiated, "
7450           "complete with any remote candidates that have been supplied via "
7451           "addIceCandidate() since the offer or answer was created",
7452           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
7453           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7454
7455   g_object_class_install_property (gobject_class,
7456       PROP_STUN_SERVER,
7457       g_param_spec_string ("stun-server", "STUN Server",
7458           "The STUN server of the form stun://hostname:port",
7459           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7460
7461   g_object_class_install_property (gobject_class,
7462       PROP_TURN_SERVER,
7463       g_param_spec_string ("turn-server", "TURN Server",
7464           "The TURN server of the form turn(s)://username:password@host:port. "
7465           "This is a convenience property, use #GstWebRTCBin::add-turn-server "
7466           "if you wish to use multiple TURN servers",
7467           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7468
7469   g_object_class_install_property (gobject_class,
7470       PROP_CONNECTION_STATE,
7471       g_param_spec_enum ("connection-state", "Connection State",
7472           "The overall connection state of this element",
7473           GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
7474           GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
7475           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7476
7477   g_object_class_install_property (gobject_class,
7478       PROP_SIGNALING_STATE,
7479       g_param_spec_enum ("signaling-state", "Signaling State",
7480           "The signaling state of this element",
7481           GST_TYPE_WEBRTC_SIGNALING_STATE,
7482           GST_WEBRTC_SIGNALING_STATE_STABLE,
7483           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7484
7485   g_object_class_install_property (gobject_class,
7486       PROP_ICE_CONNECTION_STATE,
7487       g_param_spec_enum ("ice-connection-state", "ICE connection state",
7488           "The collective connection state of all ICETransport's",
7489           GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
7490           GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
7491           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7492
7493   g_object_class_install_property (gobject_class,
7494       PROP_ICE_GATHERING_STATE,
7495       g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
7496           "The collective gathering state of all ICETransport's",
7497           GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
7498           GST_WEBRTC_ICE_GATHERING_STATE_NEW,
7499           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7500
7501   g_object_class_install_property (gobject_class,
7502       PROP_BUNDLE_POLICY,
7503       g_param_spec_enum ("bundle-policy", "Bundle Policy",
7504           "The policy to apply for bundling",
7505           GST_TYPE_WEBRTC_BUNDLE_POLICY,
7506           GST_WEBRTC_BUNDLE_POLICY_NONE,
7507           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7508
7509   g_object_class_install_property (gobject_class,
7510       PROP_ICE_TRANSPORT_POLICY,
7511       g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
7512           "The policy to apply for ICE transport",
7513           GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
7514           GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
7515           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7516
7517   g_object_class_install_property (gobject_class,
7518       PROP_ICE_AGENT,
7519       g_param_spec_object ("ice-agent", "WebRTC ICE agent",
7520           "The WebRTC ICE agent",
7521           GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7522
7523   /**
7524    * GstWebRTCBin:latency:
7525    *
7526    * Default duration to buffer in the jitterbuffers (in ms)
7527    *
7528    * Since: 1.18
7529    */
7530
7531   g_object_class_install_property (gobject_class,
7532       PROP_LATENCY,
7533       g_param_spec_uint ("latency", "Latency",
7534           "Default duration to buffer in the jitterbuffers (in ms)",
7535           0, G_MAXUINT, DEFAULT_JB_LATENCY,
7536           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
7537
7538   /**
7539    * GstWebRTCBin:sctp-transport:
7540    *
7541    * The WebRTC SCTP Transport
7542    *
7543    * Since: 1.20
7544    */
7545   g_object_class_install_property (gobject_class,
7546       PROP_SCTP_TRANSPORT,
7547       g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
7548           "The WebRTC SCTP Transport",
7549           GST_TYPE_WEBRTC_SCTP_TRANSPORT,
7550           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
7551
7552   /**
7553    * GstWebRTCBin::create-offer:
7554    * @object: the #webrtcbin
7555    * @options: (nullable): create-offer options
7556    * @promise: a #GstPromise which will contain the offer
7557    */
7558   gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
7559       g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
7560       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7561       G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
7562       G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7563
7564   /**
7565    * GstWebRTCBin::create-answer:
7566    * @object: the #webrtcbin
7567    * @options: (nullable): create-answer options
7568    * @promise: a #GstPromise which will contain the answer
7569    */
7570   gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
7571       g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
7572       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7573       G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
7574       G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
7575
7576   /**
7577    * GstWebRTCBin::set-local-description:
7578    * @object: the #GstWebRTCBin
7579    * @desc: a #GstWebRTCSessionDescription description
7580    * @promise: (nullable): a #GstPromise to be notified when it's set
7581    */
7582   gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
7583       g_signal_new_class_handler ("set-local-description",
7584       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7585       G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
7586       G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7587
7588   /**
7589    * GstWebRTCBin::set-remote-description:
7590    * @object: the #GstWebRTCBin
7591    * @desc: a #GstWebRTCSessionDescription description
7592    * @promise: (nullable): a #GstPromise to be notified when it's set
7593    */
7594   gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
7595       g_signal_new_class_handler ("set-remote-description",
7596       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7597       G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
7598       G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
7599
7600   /**
7601    * GstWebRTCBin::add-ice-candidate:
7602    * @object: the #webrtcbin
7603    * @mline_index: the index of the media description in the SDP
7604    * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
7605    * will arrive
7606    */
7607   gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
7608       g_signal_new_class_handler ("add-ice-candidate",
7609       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7610       G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
7611       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7612
7613   /**
7614    * GstWebRTCBin::get-stats:
7615    * @object: the #webrtcbin
7616    * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
7617    * @promise: a #GstPromise for the result
7618    *
7619    * The @promise will contain the result of retrieving the session statistics.
7620    * The structure will be named 'application/x-webrtc-stats and contain the
7621    * following based on the webrtc-stats spec available from
7622    * https://www.w3.org/TR/webrtc-stats/.  As the webrtc-stats spec is a draft
7623    * and is constantly changing these statistics may be changed to fit with
7624    * the latest spec.
7625    *
7626    * Each field key is a unique identifier for each RTCStats
7627    * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
7628    * GstStructure) in the RTCStatsReport
7629    * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object).  Each supported
7630    * field in the RTCStats subclass is outlined below.
7631    *
7632    * Each statistics structure contains the following values as defined by
7633    * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
7634    *
7635    *  "timestamp"           G_TYPE_DOUBLE               timestamp the statistics were generated
7636    *  "type"                GST_TYPE_WEBRTC_STATS_TYPE  the type of statistics reported
7637    *  "id"                  G_TYPE_STRING               unique identifier
7638    *
7639    * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
7640    *
7641    *  "payload-type"        G_TYPE_UINT                 the rtp payload number in use
7642    *  "clock-rate"          G_TYPE_UINT                 the rtp clock-rate
7643    *
7644    * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
7645    *
7646    *  "ssrc"                G_TYPE_STRING               the rtp sequence src in use
7647    *  "transport-id"        G_TYPE_STRING               identifier for the associated RTCTransportStats for this stream
7648    *  "codec-id"            G_TYPE_STRING               identifier for the associated RTCCodecStats for this stream
7649    *  "fir-count"           G_TYPE_UINT                 FIR requests received by the sender (only for local statistics)
7650    *  "pli-count"           G_TYPE_UINT                 PLI requests received by the sender (only for local statistics)
7651    *  "nack-count"          G_TYPE_UINT                 NACK requests received by the sender (only for local statistics)
7652    *
7653    * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
7654    *
7655    *  "packets-received"     G_TYPE_UINT64              number of packets received (only for local inbound)
7656    *  "bytes-received"       G_TYPE_UINT64              number of bytes received (only for local inbound)
7657    *  "packets-lost"         G_TYPE_UINT                number of packets lost
7658    *  "jitter"               G_TYPE_DOUBLE              packet jitter measured in secondss
7659    *
7660    * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
7661    *
7662    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteOutboundRTPStreamStats
7663    *
7664    * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
7665    *
7666    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCOutboundRTPSTreamStats
7667    *  "round-trip-time"     G_TYPE_DOUBLE               round trip time of packets measured in seconds
7668    *
7669    * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
7670    *
7671    *  "packets-sent"        G_TYPE_UINT64               number of packets sent (only for local outbound)
7672    *  "bytes-sent"          G_TYPE_UINT64               number of packets sent (only for local outbound)
7673    *
7674    * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
7675    *
7676    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteInboundRTPSTreamStats
7677    *
7678    * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
7679    *
7680    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCInboundRTPSTreamStats
7681    *
7682    */
7683   gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
7684       g_signal_new_class_handler ("get-stats",
7685       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7686       G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
7687       G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
7688
7689   /**
7690    * GstWebRTCBin::on-negotiation-needed:
7691    * @object: the #webrtcbin
7692    */
7693   gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
7694       g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
7695       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
7696
7697   /**
7698    * GstWebRTCBin::on-ice-candidate:
7699    * @object: the #webrtcbin
7700    * @mline_index: the index of the media description in the SDP
7701    * @candidate: the ICE candidate
7702    */
7703   gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
7704       g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
7705       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7706       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
7707
7708   /**
7709    * GstWebRTCBin::on-new-transceiver:
7710    * @object: the #webrtcbin
7711    * @candidate: the new #GstWebRTCRTPTransceiver
7712    */
7713   gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
7714       g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
7715       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7716       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
7717
7718   /**
7719    * GstWebRTCBin::on-data-channel:
7720    * @object: the #GstWebRTCBin
7721    * @candidate: the new `GstWebRTCDataChannel`
7722    */
7723   gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
7724       g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
7725       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
7726       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
7727
7728   /**
7729    * GstWebRTCBin::add-transceiver:
7730    * @object: the #webrtcbin
7731    * @direction: the direction of the new transceiver
7732    * @caps: (allow none): the codec preferences for this transceiver
7733    *
7734    * Returns: the new #GstWebRTCRTPTransceiver
7735    */
7736   gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
7737       g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
7738       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7739       G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
7740       NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
7741       GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
7742
7743   /**
7744    * GstWebRTCBin::get-transceivers:
7745    * @object: the #webrtcbin
7746    *
7747    * Returns: a #GArray of #GstWebRTCRTPTransceivers
7748    */
7749   gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
7750       g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
7751       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7752       G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
7753       G_TYPE_ARRAY, 0);
7754
7755   /**
7756    * GstWebRTCBin::get-transceiver:
7757    * @object: the #GstWebRTCBin
7758    * @idx: The index of the transceiver
7759    *
7760    * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
7761    * Since: 1.16
7762    */
7763   gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
7764       g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
7765       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7766       G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
7767       GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
7768
7769   /**
7770    * GstWebRTCBin::add-turn-server:
7771    * @object: the #GstWebRTCBin
7772    * @uri: The uri of the server of the form turn(s)://username:password@host:port
7773    *
7774    * Add a turn server to obtain ICE candidates from
7775    */
7776   gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
7777       g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
7778       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7779       G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
7780       G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
7781
7782   /*
7783    * GstWebRTCBin::create-data-channel:
7784    * @object: the #GstWebRTCBin
7785    * @label: the label for the data channel
7786    * @options: a #GstStructure of options for creating the data channel
7787    *
7788    * The options dictionary is the same format as the RTCDataChannelInit
7789    * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
7790    * and reproduced below
7791    *
7792    *  ordered               G_TYPE_BOOLEAN        Whether the channal will send data with guaranteed ordering
7793    *  max-packet-lifetime   G_TYPE_INT            The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
7794    *  max-retransmits       G_TYPE_INT            The number of times data will be attempted to be transmitted without acknowledgement before dropping
7795    *  protocol              G_TYPE_STRING         The subprotocol used by this channel
7796    *  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.
7797    *  id                    G_TYPE_INT            Override the default identifier selection of this channel
7798    *  priority              GST_TYPE_WEBRTC_PRIORITY_TYPE   The priority to use for this channel
7799    *
7800    * Returns: (transfer full): a new data channel object
7801    */
7802   gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
7803       g_signal_new_class_handler ("create-data-channel",
7804       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
7805       G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
7806       NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
7807
7808   gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
7809   gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0);
7810 }
7811
7812 static void
7813 _unparent_and_unref (GObject * object)
7814 {
7815   GstObject *obj = GST_OBJECT (object);
7816
7817   GST_OBJECT_PARENT (obj) = NULL;
7818
7819   gst_object_unref (obj);
7820 }
7821
7822 static void
7823 _transport_free (GObject * object)
7824 {
7825   TransportStream *stream = (TransportStream *) object;
7826   GstWebRTCBin *webrtc;
7827
7828   webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
7829
7830   if (stream->transport) {
7831     g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
7832     g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
7833   }
7834
7835   gst_object_unref (object);
7836 }
7837
7838 static void
7839 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
7840 {
7841   /* Set SINK/SRC flags as webrtcbin can act as one depending on the
7842    * SDP later. Without setting this here already, surrounding bins might not
7843    * notice this and the pipeline configuration might become inconsistent,
7844    * e.g. with regards to latency.
7845    * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
7846    */
7847   gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
7848       GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
7849   GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
7850
7851   webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
7852   g_mutex_init (PC_GET_LOCK (webrtc));
7853   g_cond_init (PC_GET_COND (webrtc));
7854
7855   g_mutex_init (ICE_GET_LOCK (webrtc));
7856   g_mutex_init (DC_GET_LOCK (webrtc));
7857
7858   webrtc->rtpbin = _create_rtpbin (webrtc);
7859   gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
7860
7861   webrtc->priv->transceivers =
7862       g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
7863   webrtc->priv->transports =
7864       g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
7865
7866   webrtc->priv->data_channels =
7867       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7868
7869   webrtc->priv->pending_data_channels =
7870       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
7871
7872   webrtc->priv->ice_stream_map =
7873       g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
7874   webrtc->priv->pending_remote_ice_candidates =
7875       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7876   g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
7877       (GDestroyNotify) _clear_ice_candidate_item);
7878
7879   webrtc->priv->pending_local_ice_candidates =
7880       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
7881   g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
7882       (GDestroyNotify) _clear_ice_candidate_item);
7883
7884   /* we start off closed until we move to READY */
7885   webrtc->priv->is_closed = TRUE;
7886   webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;
7887 }