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