5e101deae019478f0b5b1961554210e876c56a38
[platform/upstream/gstreamer.git] / 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 "sctptransport.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #define RANDOM_SESSION_ID \
39     ((((((guint64) g_random_int()) << 32) | \
40        (guint64) g_random_int ())) & \
41     G_GUINT64_CONSTANT (0x7fffffffffffffff))
42
43 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
44 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
45 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
46
47 #define PC_GET_COND(w) (&w->priv->pc_cond)
48 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
49 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
50 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
51
52 /*
53  * This webrtcbin implements the majority of the W3's peerconnection API and
54  * implementation guide where possible. Generating offers, answers and setting
55  * local and remote SDP's are all supported.  Both media descriptions and
56  * descriptions involving data channels are supported.
57  *
58  * Each input/output pad is equivalent to a Track in W3 parlance which are
59  * added/removed from the bin.  The number of requested sink pads is the number
60  * of streams that will be sent to the receiver and will be associated with a
61  * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
62  *
63  * On the receiving side, RTPTransceiver's are created in response to setting
64  * a remote description.  Output pads for the receiving streams in the set
65  * description are also created when data is received.
66  *
67  * A TransportStream is created when needed in order to transport the data over
68  * the necessary DTLS/ICE channel to the peer.  The exact configuration depends
69  * on the negotiated SDP's between the peers based on the bundle and rtcp
70  * configuration.  Some cases are outlined below for a simple single
71  * audio/video/data session:
72  *
73  * - max-bundle (requires rtcp-muxing) uses a single transport for all
74  *   media/data transported.  Renegotiation involves adding/removing the
75  *   necessary streams to the existing transports.
76  * - max-compat without rtcp-mux involves two TransportStream per media stream
77  *   to transport the rtp and the rtcp packets and a single TransportStream for
78  *   all data channels.  Each stream change involves modifying the associated
79  *   TransportStream/s as necessary.
80  */
81
82 /*
83  * TODO:
84  * assert sending payload type matches the stream
85  * reconfiguration (of anything)
86  * LS groups
87  * balanced bundle policy
88  * setting custom DTLS certificates
89  *
90  * seperate session id's from mlineindex properly
91  * how to deal with replacing a input/output track/stream
92  */
93
94 static void _update_need_negotiation (GstWebRTCBin * webrtc);
95
96 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
97 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
98
99 enum
100 {
101   PROP_PAD_TRANSCEIVER = 1,
102 };
103
104 static gboolean
105 _have_nice_elements (GstWebRTCBin * webrtc)
106 {
107   GstPluginFeature *feature;
108
109   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
110   if (feature) {
111     gst_object_unref (feature);
112   } else {
113     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
114         ("%s", "libnice elements are not available"));
115     return FALSE;
116   }
117
118   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
119   if (feature) {
120     gst_object_unref (feature);
121   } else {
122     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
123         ("%s", "libnice elements are not available"));
124     return FALSE;
125   }
126
127   return TRUE;
128 }
129
130 static gboolean
131 _have_sctp_elements (GstWebRTCBin * webrtc)
132 {
133   GstPluginFeature *feature;
134
135   feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
136   if (feature) {
137     gst_object_unref (feature);
138   } else {
139     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
140         ("%s", "sctp elements are not available"));
141     return FALSE;
142   }
143
144   feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
145   if (feature) {
146     gst_object_unref (feature);
147   } else {
148     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
149         ("%s", "sctp elements are not available"));
150     return FALSE;
151   }
152
153   return TRUE;
154 }
155
156 static gboolean
157 _have_dtls_elements (GstWebRTCBin * webrtc)
158 {
159   GstPluginFeature *feature;
160
161   feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
162   if (feature) {
163     gst_object_unref (feature);
164   } else {
165     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
166         ("%s", "dtls elements are not available"));
167     return FALSE;
168   }
169
170   feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
171   if (feature) {
172     gst_object_unref (feature);
173   } else {
174     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
175         ("%s", "dtls elements are not available"));
176     return FALSE;
177   }
178
179   return TRUE;
180 }
181
182 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
183
184 static void
185 gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
186     const GValue * value, GParamSpec * pspec)
187 {
188   switch (prop_id) {
189     default:
190       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
191       break;
192   }
193 }
194
195 static void
196 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
197     GValue * value, GParamSpec * pspec)
198 {
199   GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
200
201   switch (prop_id) {
202     case PROP_PAD_TRANSCEIVER:
203       g_value_set_object (value, pad->trans);
204       break;
205     default:
206       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
207       break;
208   }
209 }
210
211 static void
212 gst_webrtc_bin_pad_finalize (GObject * object)
213 {
214   GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
215
216   if (pad->trans)
217     gst_object_unref (pad->trans);
218   pad->trans = NULL;
219
220   if (pad->received_caps)
221     gst_caps_unref (pad->received_caps);
222   pad->received_caps = NULL;
223
224   G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
225 }
226
227 static void
228 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
229 {
230   GObjectClass *gobject_class = (GObjectClass *) klass;
231
232   gobject_class->get_property = gst_webrtc_bin_pad_get_property;
233   gobject_class->set_property = gst_webrtc_bin_pad_set_property;
234   gobject_class->finalize = gst_webrtc_bin_pad_finalize;
235
236   g_object_class_install_property (gobject_class,
237       PROP_PAD_TRANSCEIVER,
238       g_param_spec_object ("transceiver", "Transceiver",
239           "Transceiver associated with this pad",
240           GST_TYPE_WEBRTC_RTP_TRANSCEIVER,
241           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
242 }
243
244 static gboolean
245 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
246 {
247   GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
248   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent);
249   gboolean check_negotiation = FALSE;
250
251   if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
252     GstCaps *caps;
253
254     gst_event_parse_caps (event, &caps);
255     check_negotiation = (!wpad->received_caps
256         || gst_caps_is_equal (wpad->received_caps, caps));
257     gst_caps_replace (&wpad->received_caps, caps);
258
259     GST_DEBUG_OBJECT (parent,
260         "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
261         GST_PTR_FORMAT, pad, check_negotiation, caps);
262   } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
263     check_negotiation = TRUE;
264   }
265
266   if (check_negotiation) {
267     PC_LOCK (webrtc);
268     _update_need_negotiation (webrtc);
269     PC_UNLOCK (webrtc);
270   }
271
272   return gst_pad_event_default (pad, parent, event);
273 }
274
275 static void
276 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
277 {
278 }
279
280 static GstWebRTCBinPad *
281 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
282 {
283   GstWebRTCBinPad *pad =
284       g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
285       direction, NULL);
286
287   gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
288
289   if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
290     gst_object_unref (pad);
291     return NULL;
292   }
293
294   GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
295       direction == GST_PAD_SRC ? "src" : "sink");
296   return pad;
297 }
298
299 #define gst_webrtc_bin_parent_class parent_class
300 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
301     G_ADD_PRIVATE (GstWebRTCBin)
302     GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
303         "webrtcbin element");
304     );
305
306 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
307     GstWebRTCBinPad * pad);
308
309 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
310     GST_PAD_SINK,
311     GST_PAD_REQUEST,
312     GST_STATIC_CAPS ("application/x-rtp"));
313
314 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
315     GST_PAD_SRC,
316     GST_PAD_SOMETIMES,
317     GST_STATIC_CAPS ("application/x-rtp"));
318
319 enum
320 {
321   SIGNAL_0,
322   CREATE_OFFER_SIGNAL,
323   CREATE_ANSWER_SIGNAL,
324   SET_LOCAL_DESCRIPTION_SIGNAL,
325   SET_REMOTE_DESCRIPTION_SIGNAL,
326   ADD_ICE_CANDIDATE_SIGNAL,
327   ON_NEGOTIATION_NEEDED_SIGNAL,
328   ON_ICE_CANDIDATE_SIGNAL,
329   ON_NEW_TRANSCEIVER_SIGNAL,
330   GET_STATS_SIGNAL,
331   ADD_TRANSCEIVER_SIGNAL,
332   GET_TRANSCEIVER_SIGNAL,
333   GET_TRANSCEIVERS_SIGNAL,
334   ADD_TURN_SERVER_SIGNAL,
335   CREATE_DATA_CHANNEL_SIGNAL,
336   ON_DATA_CHANNEL_SIGNAL,
337   LAST_SIGNAL,
338 };
339
340 enum
341 {
342   PROP_0,
343   PROP_CONNECTION_STATE,
344   PROP_SIGNALING_STATE,
345   PROP_ICE_GATHERING_STATE,
346   PROP_ICE_CONNECTION_STATE,
347   PROP_LOCAL_DESCRIPTION,
348   PROP_CURRENT_LOCAL_DESCRIPTION,
349   PROP_PENDING_LOCAL_DESCRIPTION,
350   PROP_REMOTE_DESCRIPTION,
351   PROP_CURRENT_REMOTE_DESCRIPTION,
352   PROP_PENDING_REMOTE_DESCRIPTION,
353   PROP_STUN_SERVER,
354   PROP_TURN_SERVER,
355   PROP_BUNDLE_POLICY,
356   PROP_ICE_TRANSPORT_POLICY,
357 };
358
359 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
360
361 typedef struct
362 {
363   guint session_id;
364   GstWebRTCICEStream *stream;
365 } IceStreamItem;
366
367 /* FIXME: locking? */
368 GstWebRTCICEStream *
369 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
370 {
371   int i;
372
373   for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
374     IceStreamItem *item =
375         &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
376
377     if (item->session_id == session_id) {
378       GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
379           "session %u", item->stream, session_id);
380       return item->stream;
381     }
382   }
383
384   GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
385       session_id);
386   return NULL;
387 }
388
389 void
390 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
391     GstWebRTCICEStream * stream)
392 {
393   IceStreamItem item = { session_id, stream };
394
395   GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
396       "session %u", stream, session_id);
397   g_array_append_val (webrtc->priv->ice_stream_map, item);
398 }
399
400 typedef struct
401 {
402   guint session_id;
403   gchar *mid;
404 } SessionMidItem;
405
406 static void
407 clear_session_mid_item (SessionMidItem * item)
408 {
409   g_free (item->mid);
410 }
411
412 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
413     gconstpointer data);
414
415 static GstWebRTCRTPTransceiver *
416 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
417     FindTransceiverFunc func)
418 {
419   int i;
420
421   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
422     GstWebRTCRTPTransceiver *transceiver =
423         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
424         i);
425
426     if (func (transceiver, data))
427       return transceiver;
428   }
429
430   return NULL;
431 }
432
433 static gboolean
434 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
435 {
436   return g_strcmp0 (trans->mid, mid) == 0;
437 }
438
439 static gboolean
440 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
441 {
442   return trans->mline == *mline;
443 }
444
445 static GstWebRTCRTPTransceiver *
446 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
447 {
448   GstWebRTCRTPTransceiver *trans;
449
450   trans = _find_transceiver (webrtc, &mlineindex,
451       (FindTransceiverFunc) transceiver_match_for_mline);
452
453   GST_TRACE_OBJECT (webrtc,
454       "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
455       mlineindex);
456
457   return trans;
458 }
459
460 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
461     gconstpointer data);
462
463 static TransportStream *
464 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
465     FindTransportFunc func)
466 {
467   int i;
468
469   for (i = 0; i < webrtc->priv->transports->len; i++) {
470     TransportStream *stream =
471         g_array_index (webrtc->priv->transports, TransportStream *,
472         i);
473
474     if (func (stream, data))
475       return stream;
476   }
477
478   return NULL;
479 }
480
481 static gboolean
482 match_stream_for_session (TransportStream * trans, guint * session)
483 {
484   return trans->session_id == *session;
485 }
486
487 static TransportStream *
488 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
489 {
490   TransportStream *stream;
491
492   stream = _find_transport (webrtc, &session_id,
493       (FindTransportFunc) match_stream_for_session);
494
495   GST_TRACE_OBJECT (webrtc,
496       "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
497
498   return stream;
499 }
500
501 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
502
503 static GstWebRTCBinPad *
504 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
505 {
506   GstElement *element = GST_ELEMENT (webrtc);
507   GList *l;
508
509   GST_OBJECT_LOCK (webrtc);
510   l = element->pads;
511   for (; l; l = g_list_next (l)) {
512     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
513       continue;
514     if (func (l->data, data)) {
515       gst_object_ref (l->data);
516       GST_OBJECT_UNLOCK (webrtc);
517       return l->data;
518     }
519   }
520
521   l = webrtc->priv->pending_pads;
522   for (; l; l = g_list_next (l)) {
523     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
524       continue;
525     if (func (l->data, data)) {
526       gst_object_ref (l->data);
527       GST_OBJECT_UNLOCK (webrtc);
528       return l->data;
529     }
530   }
531   GST_OBJECT_UNLOCK (webrtc);
532
533   return NULL;
534 }
535
536 typedef gboolean (*FindDataChannelFunc) (GstWebRTCDataChannel * p1,
537     gconstpointer data);
538
539 static GstWebRTCDataChannel *
540 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
541     FindDataChannelFunc func)
542 {
543   int i;
544
545   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
546     GstWebRTCDataChannel *channel =
547         g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
548         i);
549
550     if (func (channel, data))
551       return channel;
552   }
553
554   return NULL;
555 }
556
557 static gboolean
558 data_channel_match_for_id (GstWebRTCDataChannel * channel, gint * id)
559 {
560   return channel->id == *id;
561 }
562
563 static GstWebRTCDataChannel *
564 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
565 {
566   GstWebRTCDataChannel *channel;
567
568   channel = _find_data_channel (webrtc, &id,
569       (FindDataChannelFunc) data_channel_match_for_id);
570
571   GST_TRACE_OBJECT (webrtc,
572       "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
573
574   return channel;
575 }
576
577 static void
578 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
579 {
580   GST_OBJECT_LOCK (webrtc);
581   webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
582   GST_OBJECT_UNLOCK (webrtc);
583 }
584
585 static void
586 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
587 {
588   GST_OBJECT_LOCK (webrtc);
589   webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
590   GST_OBJECT_UNLOCK (webrtc);
591 }
592
593 static void
594 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
595 {
596   _remove_pending_pad (webrtc, pad);
597
598   if (webrtc->priv->running)
599     gst_pad_set_active (GST_PAD (pad), TRUE);
600   gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
601 }
602
603 static void
604 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
605 {
606   _remove_pending_pad (webrtc, pad);
607
608   gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
609 }
610
611 typedef struct
612 {
613   GstPadDirection direction;
614   guint mlineindex;
615 } MLineMatch;
616
617 static gboolean
618 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
619 {
620   return GST_PAD_DIRECTION (pad) == match->direction
621       && pad->mlineindex == match->mlineindex;
622 }
623
624 static GstWebRTCBinPad *
625 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
626     guint mlineindex)
627 {
628   MLineMatch m = { direction, mlineindex };
629
630   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
631 }
632
633 typedef struct
634 {
635   GstPadDirection direction;
636   GstWebRTCRTPTransceiver *trans;
637 } TransMatch;
638
639 static gboolean
640 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
641 {
642   return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
643 }
644
645 static GstWebRTCBinPad *
646 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
647     GstWebRTCRTPTransceiver * trans)
648 {
649   TransMatch m = { direction, trans };
650
651   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
652 }
653
654 #if 0
655 static gboolean
656 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
657 {
658   return pad->ssrc == *ssrc;
659 }
660
661 static gboolean
662 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
663 {
664   return pad == other;
665 }
666 #endif
667
668 static gboolean
669 _unlock_pc_thread (GMutex * lock)
670 {
671   g_mutex_unlock (lock);
672   return G_SOURCE_REMOVE;
673 }
674
675 static gpointer
676 _gst_pc_thread (GstWebRTCBin * webrtc)
677 {
678   PC_LOCK (webrtc);
679   webrtc->priv->main_context = g_main_context_new ();
680   webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
681
682   PC_COND_BROADCAST (webrtc);
683   g_main_context_invoke (webrtc->priv->main_context,
684       (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
685
686   /* Having the thread be the thread default GMainContext will break the
687    * required queue-like ordering (from W3's peerconnection spec) of re-entrant
688    * tasks */
689   g_main_loop_run (webrtc->priv->loop);
690
691   PC_LOCK (webrtc);
692   g_main_context_unref (webrtc->priv->main_context);
693   webrtc->priv->main_context = NULL;
694   g_main_loop_unref (webrtc->priv->loop);
695   webrtc->priv->loop = NULL;
696   PC_COND_BROADCAST (webrtc);
697   PC_UNLOCK (webrtc);
698
699   return NULL;
700 }
701
702 static void
703 _start_thread (GstWebRTCBin * webrtc)
704 {
705   PC_LOCK (webrtc);
706   webrtc->priv->thread = g_thread_new ("gst-pc-ops",
707       (GThreadFunc) _gst_pc_thread, webrtc);
708
709   while (!webrtc->priv->loop)
710     PC_COND_WAIT (webrtc);
711   webrtc->priv->is_closed = FALSE;
712   PC_UNLOCK (webrtc);
713 }
714
715 static void
716 _stop_thread (GstWebRTCBin * webrtc)
717 {
718   PC_LOCK (webrtc);
719   webrtc->priv->is_closed = TRUE;
720   g_main_loop_quit (webrtc->priv->loop);
721   while (webrtc->priv->loop)
722     PC_COND_WAIT (webrtc);
723   PC_UNLOCK (webrtc);
724
725   g_thread_unref (webrtc->priv->thread);
726 }
727
728 static gboolean
729 _execute_op (GstWebRTCBinTask * op)
730 {
731   PC_LOCK (op->webrtc);
732   if (op->webrtc->priv->is_closed) {
733     GST_DEBUG_OBJECT (op->webrtc,
734         "Peerconnection is closed, aborting execution");
735     goto out;
736   }
737
738   op->op (op->webrtc, op->data);
739
740 out:
741   PC_UNLOCK (op->webrtc);
742   return G_SOURCE_REMOVE;
743 }
744
745 static void
746 _free_op (GstWebRTCBinTask * op)
747 {
748   if (op->notify)
749     op->notify (op->data);
750   g_free (op);
751 }
752
753 void
754 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
755     gpointer data, GDestroyNotify notify)
756 {
757   GstWebRTCBinTask *op;
758   GSource *source;
759
760   g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc));
761
762   if (webrtc->priv->is_closed) {
763     GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
764     if (notify)
765       notify (data);
766     return;
767   }
768   op = g_new0 (GstWebRTCBinTask, 1);
769   op->webrtc = webrtc;
770   op->op = func;
771   op->data = data;
772   op->notify = notify;
773
774   source = g_idle_source_new ();
775   g_source_set_priority (source, G_PRIORITY_DEFAULT);
776   g_source_set_callback (source, (GSourceFunc) _execute_op, op,
777       (GDestroyNotify) _free_op);
778   g_source_attach (source, webrtc->priv->main_context);
779   g_source_unref (source);
780 }
781
782 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
783 static GstWebRTCICEConnectionState
784 _collate_ice_connection_states (GstWebRTCBin * webrtc)
785 {
786 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
787   GstWebRTCICEConnectionState any_state = 0;
788   gboolean all_closed = TRUE;
789   int i;
790
791   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
792     GstWebRTCRTPTransceiver *rtp_trans =
793         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
794         i);
795     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
796     TransportStream *stream = trans->stream;
797     GstWebRTCICETransport *transport, *rtcp_transport;
798     GstWebRTCICEConnectionState ice_state;
799     gboolean rtcp_mux = FALSE;
800
801     if (rtp_trans->stopped)
802       continue;
803     if (!rtp_trans->mid)
804       continue;
805
806     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
807
808     transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
809
810     /* get transport state */
811     g_object_get (transport, "state", &ice_state, NULL);
812     any_state |= (1 << ice_state);
813     if (ice_state != STATE (CLOSED))
814       all_closed = FALSE;
815
816     rtcp_transport =
817         webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport;
818
819     if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) {
820       g_object_get (rtcp_transport, "state", &ice_state, NULL);
821       any_state |= (1 << ice_state);
822       if (ice_state != STATE (CLOSED))
823         all_closed = FALSE;
824     }
825   }
826
827   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
828
829   if (webrtc->priv->is_closed) {
830     GST_TRACE_OBJECT (webrtc, "returning closed");
831     return STATE (CLOSED);
832   }
833   /* Any of the RTCIceTransport s are in the failed state. */
834   if (any_state & (1 << STATE (FAILED))) {
835     GST_TRACE_OBJECT (webrtc, "returning failed");
836     return STATE (FAILED);
837   }
838   /* Any of the RTCIceTransport s are in the disconnected state and
839    * none of them are in the failed state. */
840   if (any_state & (1 << STATE (DISCONNECTED))) {
841     GST_TRACE_OBJECT (webrtc, "returning disconnected");
842     return STATE (DISCONNECTED);
843   }
844   /* Any of the RTCIceTransport's are in the checking state and none of them
845    * are in the failed or disconnected state. */
846   if (any_state & (1 << STATE (CHECKING))) {
847     GST_TRACE_OBJECT (webrtc, "returning checking");
848     return STATE (CHECKING);
849   }
850   /* Any of the RTCIceTransport s are in the new state and none of them are
851    * in the checking, failed or disconnected state, or all RTCIceTransport's
852    * are in the closed state. */
853   if ((any_state & (1 << STATE (NEW))) || all_closed) {
854     GST_TRACE_OBJECT (webrtc, "returning new");
855     return STATE (NEW);
856   }
857   /* All RTCIceTransport s are in the connected, completed or closed state
858    * and at least one of them is in the connected state. */
859   if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 <<
860           STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) {
861     GST_TRACE_OBJECT (webrtc, "returning connected");
862     return STATE (CONNECTED);
863   }
864   /* All RTCIceTransport s are in the completed or closed state and at least
865    * one of them is in the completed state. */
866   if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED))
867       && any_state & (1 << STATE (COMPLETED))) {
868     GST_TRACE_OBJECT (webrtc, "returning connected");
869     return STATE (CONNECTED);
870   }
871
872   GST_FIXME ("unspecified situation, returning new");
873   return STATE (NEW);
874 #undef STATE
875 }
876
877 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
878 static GstWebRTCICEGatheringState
879 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
880 {
881 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
882   GstWebRTCICEGatheringState any_state = 0;
883   gboolean all_completed = webrtc->priv->transceivers->len > 0;
884   int i;
885
886   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
887     GstWebRTCRTPTransceiver *rtp_trans =
888         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
889         i);
890     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
891     TransportStream *stream = trans->stream;
892     GstWebRTCICETransport *transport, *rtcp_transport;
893     GstWebRTCICEGatheringState ice_state;
894     gboolean rtcp_mux = FALSE;
895
896     if (rtp_trans->stopped)
897       continue;
898     if (!rtp_trans->mid)
899       continue;
900
901     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
902
903     transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
904
905     /* get gathering state */
906     g_object_get (transport, "gathering-state", &ice_state, NULL);
907     any_state |= (1 << ice_state);
908     if (ice_state != STATE (COMPLETE))
909       all_completed = FALSE;
910
911     rtcp_transport =
912         webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport;
913
914     if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
915       g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL);
916       any_state |= (1 << ice_state);
917       if (ice_state != STATE (COMPLETE))
918         all_completed = FALSE;
919     }
920   }
921
922   GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
923
924   /* Any of the RTCIceTransport s are in the gathering state. */
925   if (any_state & (1 << STATE (GATHERING))) {
926     GST_TRACE_OBJECT (webrtc, "returning gathering");
927     return STATE (GATHERING);
928   }
929   /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
930    * the completed gathering state. */
931   if (all_completed) {
932     GST_TRACE_OBJECT (webrtc, "returning complete");
933     return STATE (COMPLETE);
934   }
935
936   /* Any of the RTCIceTransport s are in the new gathering state and none
937    * of the transports are in the gathering state, or there are no transports. */
938   GST_TRACE_OBJECT (webrtc, "returning new");
939   return STATE (NEW);
940 #undef STATE
941 }
942
943 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
944 static GstWebRTCPeerConnectionState
945 _collate_peer_connection_states (GstWebRTCBin * webrtc)
946 {
947 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
948 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
949 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
950   GstWebRTCICEConnectionState any_ice_state = 0;
951   GstWebRTCDTLSTransportState any_dtls_state = 0;
952   int i;
953
954   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
955     GstWebRTCRTPTransceiver *rtp_trans =
956         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
957         i);
958     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
959     TransportStream *stream = trans->stream;
960     GstWebRTCDTLSTransport *transport, *rtcp_transport;
961     GstWebRTCICEGatheringState ice_state;
962     GstWebRTCDTLSTransportState dtls_state;
963     gboolean rtcp_mux = FALSE;
964
965     if (rtp_trans->stopped)
966       continue;
967     if (!rtp_trans->mid)
968       continue;
969
970     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
971     transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
972
973     /* get transport state */
974     g_object_get (transport, "state", &dtls_state, NULL);
975     any_dtls_state |= (1 << dtls_state);
976     g_object_get (transport->transport, "state", &ice_state, NULL);
977     any_ice_state |= (1 << ice_state);
978
979     rtcp_transport = webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans);
980
981     if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
982       g_object_get (rtcp_transport, "state", &dtls_state, NULL);
983       any_dtls_state |= (1 << dtls_state);
984       g_object_get (rtcp_transport->transport, "state", &ice_state, NULL);
985       any_ice_state |= (1 << ice_state);
986     }
987   }
988
989   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
990       "state: 0x%x", any_ice_state, any_dtls_state);
991
992   /* The RTCPeerConnection object's [[ isClosed]] slot is true.  */
993   if (webrtc->priv->is_closed) {
994     GST_TRACE_OBJECT (webrtc, "returning closed");
995     return STATE (CLOSED);
996   }
997
998   /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
999   if (any_ice_state & (1 << ICE_STATE (FAILED))) {
1000     GST_TRACE_OBJECT (webrtc, "returning failed");
1001     return STATE (FAILED);
1002   }
1003   if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
1004     GST_TRACE_OBJECT (webrtc, "returning failed");
1005     return STATE (FAILED);
1006   }
1007
1008   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting
1009    * or checking state and none of them is in the failed state. */
1010   if (any_ice_state & (1 << ICE_STATE (CHECKING))) {
1011     GST_TRACE_OBJECT (webrtc, "returning connecting");
1012     return STATE (CONNECTING);
1013   }
1014   if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) {
1015     GST_TRACE_OBJECT (webrtc, "returning connecting");
1016     return STATE (CONNECTING);
1017   }
1018
1019   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
1020    * state and none of them are in the failed or connecting or checking state. */
1021   if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
1022     GST_TRACE_OBJECT (webrtc, "returning disconnected");
1023     return STATE (DISCONNECTED);
1024   }
1025
1026   /* All RTCIceTransport's and RTCDtlsTransport's are in the connected,
1027    * completed or closed state and at least of them is in the connected or
1028    * completed state. */
1029   if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 <<
1030               ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED)))
1031       && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 <<
1032               DTLS_STATE (CLOSED)))
1033       && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 <<
1034               ICE_STATE (COMPLETED))
1035           || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) {
1036     GST_TRACE_OBJECT (webrtc, "returning connected");
1037     return STATE (CONNECTED);
1038   }
1039
1040   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state
1041    * and none of the transports are in the connecting, checking, failed or
1042    * disconnected state, or all transports are in the closed state. */
1043   if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) {
1044     GST_TRACE_OBJECT (webrtc, "returning new");
1045     return STATE (NEW);
1046   }
1047   if ((any_ice_state & (1 << ICE_STATE (NEW))
1048           || any_dtls_state & (1 << DTLS_STATE (NEW)))
1049       && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED)
1050               | (1 << ICE_STATE (DISCONNECTED))))
1051       && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 <<
1052               DTLS_STATE (FAILED)))) {
1053     GST_TRACE_OBJECT (webrtc, "returning new");
1054     return STATE (NEW);
1055   }
1056
1057   GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new");
1058   return STATE (NEW);
1059 #undef DTLS_STATE
1060 #undef ICE_STATE
1061 #undef STATE
1062 }
1063
1064 static void
1065 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1066 {
1067   GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1068   GstWebRTCICEGatheringState new_state;
1069
1070   new_state = _collate_ice_gathering_states (webrtc);
1071
1072   if (new_state != webrtc->ice_gathering_state) {
1073     gchar *old_s, *new_s;
1074
1075     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1076         old_state);
1077     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1078         new_state);
1079     GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1080         old_s, old_state, new_s, new_state);
1081     g_free (old_s);
1082     g_free (new_s);
1083
1084     webrtc->ice_gathering_state = new_state;
1085     PC_UNLOCK (webrtc);
1086     g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1087     PC_LOCK (webrtc);
1088   }
1089 }
1090
1091 static void
1092 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1093 {
1094   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1095       NULL);
1096 }
1097
1098 static void
1099 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1100 {
1101   GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1102   GstWebRTCICEConnectionState new_state;
1103
1104   new_state = _collate_ice_connection_states (webrtc);
1105
1106   if (new_state != old_state) {
1107     gchar *old_s, *new_s;
1108
1109     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1110         old_state);
1111     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1112         new_state);
1113     GST_INFO_OBJECT (webrtc,
1114         "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1115         new_s, new_state);
1116     g_free (old_s);
1117     g_free (new_s);
1118
1119     webrtc->ice_connection_state = new_state;
1120     PC_UNLOCK (webrtc);
1121     g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1122     PC_LOCK (webrtc);
1123   }
1124 }
1125
1126 static void
1127 _update_ice_connection_state (GstWebRTCBin * webrtc)
1128 {
1129   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1130       NULL);
1131 }
1132
1133 static void
1134 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1135 {
1136   GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1137   GstWebRTCPeerConnectionState new_state;
1138
1139   new_state = _collate_peer_connection_states (webrtc);
1140
1141   if (new_state != old_state) {
1142     gchar *old_s, *new_s;
1143
1144     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1145         old_state);
1146     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1147         new_state);
1148     GST_INFO_OBJECT (webrtc,
1149         "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1150         new_s, new_state);
1151     g_free (old_s);
1152     g_free (new_s);
1153
1154     webrtc->peer_connection_state = new_state;
1155     PC_UNLOCK (webrtc);
1156     g_object_notify (G_OBJECT (webrtc), "connection-state");
1157     PC_LOCK (webrtc);
1158   }
1159 }
1160
1161 static void
1162 _update_peer_connection_state (GstWebRTCBin * webrtc)
1163 {
1164   gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1165       NULL, NULL);
1166 }
1167
1168 static gboolean
1169 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1170 {
1171   GList *l;
1172   gboolean res = FALSE;
1173
1174   GST_OBJECT_LOCK (webrtc);
1175   l = GST_ELEMENT (webrtc)->pads;
1176   for (; l; l = g_list_next (l)) {
1177     GstWebRTCBinPad *wpad;
1178
1179     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1180       continue;
1181
1182     wpad = GST_WEBRTC_BIN_PAD (l->data);
1183     if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps
1184         && (!wpad->trans || !wpad->trans->stopped)) {
1185       goto done;
1186     }
1187   }
1188
1189   l = webrtc->priv->pending_pads;
1190   for (; l; l = g_list_next (l)) {
1191     if (!GST_IS_WEBRTC_BIN_PAD (l->data)) {
1192       goto done;
1193     }
1194   }
1195
1196   res = TRUE;
1197
1198 done:
1199   GST_OBJECT_UNLOCK (webrtc);
1200   return res;
1201 }
1202
1203 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1204 static gboolean
1205 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1206 {
1207   int i;
1208
1209   GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1210
1211   /* We can't negotiate until we have received caps on all our sink pads,
1212    * as we will need the ssrcs in our offer / answer */
1213   if (!_all_sinks_have_caps (webrtc)) {
1214     GST_LOG_OBJECT (webrtc,
1215         "no negotiation possible until caps have been received on all sink pads");
1216     return FALSE;
1217   }
1218
1219   /* If any implementation-specific negotiation is required, as described at
1220    * the start of this section, return "true".
1221    * FIXME */
1222   /* FIXME: emit when input caps/format changes? */
1223
1224   if (!webrtc->current_local_description) {
1225     GST_LOG_OBJECT (webrtc, "no local description set");
1226     return TRUE;
1227   }
1228
1229   if (!webrtc->current_remote_description) {
1230     GST_LOG_OBJECT (webrtc, "no remote description set");
1231     return TRUE;
1232   }
1233
1234   /* If connection has created any RTCDataChannel's, and no m= section has
1235    * been negotiated yet for data, return "true". */
1236   if (webrtc->priv->data_channels->len > 0) {
1237     if (_message_get_datachannel_index (webrtc->current_local_description->
1238             sdp) >= G_MAXUINT) {
1239       GST_LOG_OBJECT (webrtc,
1240           "no data channel media section and have %u " "transports",
1241           webrtc->priv->data_channels->len);
1242       return TRUE;
1243     }
1244   }
1245
1246   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1247     GstWebRTCRTPTransceiver *trans;
1248
1249     trans =
1250         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1251         i);
1252
1253     if (trans->stopped) {
1254       /* FIXME: If t is stopped and is associated with an m= section according to
1255        * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1256        * rejected in connection's currentLocalDescription or
1257        * currentRemoteDescription , return "true". */
1258       GST_FIXME_OBJECT (webrtc,
1259           "check if the transceiver is rejected in descriptions");
1260     } else {
1261       const GstSDPMedia *media;
1262       GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1263
1264       if (trans->mline == -1 || trans->mid == NULL) {
1265         GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT
1266             " mid %s", i, trans, trans->mid);
1267         return TRUE;
1268       }
1269       /* internal inconsistency */
1270       g_assert (trans->mline <
1271           gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1272       g_assert (trans->mline <
1273           gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1274
1275       /* FIXME: msid handling
1276        * If t's direction is "sendrecv" or "sendonly", and the associated m=
1277        * section in connection's currentLocalDescription doesn't contain an
1278        * "a=msid" line, return "true". */
1279
1280       media =
1281           gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1282           trans->mline);
1283       local_dir = _get_direction_from_media (media);
1284
1285       media =
1286           gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1287           trans->mline);
1288       remote_dir = _get_direction_from_media (media);
1289
1290       if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1291         /* If connection's currentLocalDescription if of type "offer", and
1292          * the direction of the associated m= section in neither the offer
1293          * nor answer matches t's direction, return "true". */
1294
1295         if (local_dir != trans->direction && remote_dir != trans->direction) {
1296           gchar *local_str, *remote_str, *dir_str;
1297
1298           local_str =
1299               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1300               local_dir);
1301           remote_str =
1302               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1303               remote_dir);
1304           dir_str =
1305               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1306               trans->direction);
1307
1308           GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1309               "description (local %s remote %s)", dir_str, local_str,
1310               remote_str);
1311
1312           g_free (dir_str);
1313           g_free (local_str);
1314           g_free (remote_str);
1315
1316           return TRUE;
1317         }
1318       } else if (webrtc->current_local_description->type ==
1319           GST_WEBRTC_SDP_TYPE_ANSWER) {
1320         GstWebRTCRTPTransceiverDirection intersect_dir;
1321
1322         /* If connection's currentLocalDescription if of type "answer", and
1323          * the direction of the associated m= section in the answer does not
1324          * match t's direction intersected with the offered direction (as
1325          * described in [JSEP] (section 5.3.1.)), return "true". */
1326
1327         /* remote is the offer, local is the answer */
1328         intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1329
1330         if (intersect_dir != trans->direction) {
1331           gchar *local_str, *remote_str, *inter_str, *dir_str;
1332
1333           local_str =
1334               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1335               local_dir);
1336           remote_str =
1337               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1338               remote_dir);
1339           dir_str =
1340               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1341               trans->direction);
1342           inter_str =
1343               _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1344               intersect_dir);
1345
1346           GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match "
1347               "description intersected direction %s (local %s remote %s)",
1348               dir_str, local_str, inter_str, remote_str);
1349
1350           g_free (dir_str);
1351           g_free (local_str);
1352           g_free (remote_str);
1353           g_free (inter_str);
1354
1355           return TRUE;
1356         }
1357       }
1358     }
1359   }
1360
1361   GST_LOG_OBJECT (webrtc, "no negotiation needed");
1362   return FALSE;
1363 }
1364
1365 static void
1366 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1367 {
1368   if (webrtc->priv->need_negotiation) {
1369     GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1370     PC_UNLOCK (webrtc);
1371     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1372         0);
1373     PC_LOCK (webrtc);
1374   }
1375 }
1376
1377 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1378 static void
1379 _update_need_negotiation (GstWebRTCBin * webrtc)
1380 {
1381   /* If connection's [[isClosed]] slot is true, abort these steps. */
1382   if (webrtc->priv->is_closed)
1383     return;
1384   /* If connection's signaling state is not "stable", abort these steps. */
1385   if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1386     return;
1387
1388   /* If the result of checking if negotiation is needed is "false", clear the
1389    * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1390    * to false, and abort these steps. */
1391   if (!_check_if_negotiation_is_needed (webrtc)) {
1392     webrtc->priv->need_negotiation = FALSE;
1393     return;
1394   }
1395   /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1396   if (webrtc->priv->need_negotiation)
1397     return;
1398   /* Set connection's [[needNegotiation]] slot to true. */
1399   webrtc->priv->need_negotiation = TRUE;
1400   /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1401    * true, fire a simple event named negotiationneeded at connection. */
1402   gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1403       NULL);
1404 }
1405
1406 static GstCaps *
1407 _find_codec_preferences (GstWebRTCBin * webrtc,
1408     GstWebRTCRTPTransceiver * rtp_trans, GstPadDirection direction,
1409     guint media_idx)
1410 {
1411   WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
1412   GstCaps *ret = NULL;
1413
1414   GST_LOG_OBJECT (webrtc, "retreiving codec preferences from %" GST_PTR_FORMAT,
1415       trans);
1416
1417   if (rtp_trans && rtp_trans->codec_preferences) {
1418     GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1419         rtp_trans->codec_preferences);
1420     ret = gst_caps_ref (rtp_trans->codec_preferences);
1421   } else {
1422     GstWebRTCBinPad *pad = NULL;
1423
1424     /* try to find a pad */
1425     if (!trans
1426         || !(pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans)))
1427       pad = _find_pad_for_mline (webrtc, direction, media_idx);
1428
1429     if (!pad) {
1430       if (trans && trans->last_configured_caps)
1431         ret = gst_caps_ref (trans->last_configured_caps);
1432     } else {
1433       GstCaps *caps = NULL;
1434
1435       if (pad->received_caps) {
1436         caps = gst_caps_ref (pad->received_caps);
1437       } else if ((caps = gst_pad_get_current_caps (GST_PAD (pad)))) {
1438         GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1439             caps);
1440       } else {
1441         if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
1442           GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1443               caps);
1444       }
1445       if (caps) {
1446         if (trans)
1447           gst_caps_replace (&trans->last_configured_caps, caps);
1448
1449         ret = caps;
1450       }
1451
1452       gst_object_unref (pad);
1453     }
1454   }
1455
1456   if (!ret)
1457     GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx);
1458
1459   return ret;
1460 }
1461
1462 static GstCaps *
1463 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1464     WebRTCTransceiver * trans, const GstCaps * caps)
1465 {
1466   GstCaps *ret;
1467   guint i;
1468
1469   ret = gst_caps_make_writable (caps);
1470
1471   for (i = 0; i < gst_caps_get_size (ret); i++) {
1472     GstStructure *s = gst_caps_get_structure (ret, i);
1473
1474     if (trans->do_nack)
1475       if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1476         gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1477
1478     if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1479       gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1480     /* FIXME: is this needed? */
1481     /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1482        gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1483
1484     /* FIXME: codec-specific paramters? */
1485   }
1486
1487   return ret;
1488 }
1489
1490 static void
1491 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1492     GParamSpec * pspec, GstWebRTCBin * webrtc)
1493 {
1494   _update_ice_connection_state (webrtc);
1495   _update_peer_connection_state (webrtc);
1496 }
1497
1498 static void
1499 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1500     GParamSpec * pspec, GstWebRTCBin * webrtc)
1501 {
1502   _update_ice_gathering_state (webrtc);
1503 }
1504
1505 static void
1506 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1507     GParamSpec * pspec, GstWebRTCBin * webrtc)
1508 {
1509   _update_peer_connection_state (webrtc);
1510 }
1511
1512 static WebRTCTransceiver *
1513 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
1514     GstWebRTCRTPTransceiverDirection direction, guint mline)
1515 {
1516   WebRTCTransceiver *trans;
1517   GstWebRTCRTPTransceiver *rtp_trans;
1518   GstWebRTCRTPSender *sender;
1519   GstWebRTCRTPReceiver *receiver;
1520
1521   sender = gst_webrtc_rtp_sender_new ();
1522   receiver = gst_webrtc_rtp_receiver_new ();
1523   trans = webrtc_transceiver_new (webrtc, sender, receiver);
1524   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1525   rtp_trans->direction = direction;
1526   rtp_trans->mline = mline;
1527
1528   g_array_append_val (webrtc->priv->transceivers, trans);
1529
1530   gst_object_unref (sender);
1531   gst_object_unref (receiver);
1532
1533   g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
1534       0, trans);
1535
1536   return trans;
1537 }
1538
1539 static TransportStream *
1540 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1541 {
1542   GstWebRTCDTLSTransport *transport;
1543   TransportStream *ret;
1544
1545   /* FIXME: how to parametrize the sender and the receiver */
1546   ret = transport_stream_new (webrtc, session_id);
1547   transport = ret->transport;
1548
1549   g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1550       G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1551   g_signal_connect (G_OBJECT (transport->transport),
1552       "notify::gathering-state",
1553       G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1554   g_signal_connect (G_OBJECT (transport), "notify::state",
1555       G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1556
1557   if ((transport = ret->rtcp_transport)) {
1558     g_signal_connect (G_OBJECT (transport->transport),
1559         "notify::state", G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1560     g_signal_connect (G_OBJECT (transport->transport),
1561         "notify::gathering-state",
1562         G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1563     g_signal_connect (G_OBJECT (transport), "notify::state",
1564         G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1565   }
1566
1567   GST_TRACE_OBJECT (webrtc,
1568       "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1569
1570   return ret;
1571 }
1572
1573 static TransportStream *
1574 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1575 {
1576   TransportStream *ret;
1577   gchar *pad_name;
1578
1579   ret = _find_transport_for_session (webrtc, session_id);
1580
1581   if (!ret) {
1582     ret = _create_transport_channel (webrtc, session_id);
1583     gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1584     gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1585     g_array_append_val (webrtc->priv->transports, ret);
1586
1587     pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1588     if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1589             GST_ELEMENT (webrtc->rtpbin), pad_name))
1590       g_warn_if_reached ();
1591     g_free (pad_name);
1592
1593     pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1594     if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1595             GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1596       g_warn_if_reached ();
1597     g_free (pad_name);
1598   }
1599
1600   gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1601   gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1602
1603   return ret;
1604 }
1605
1606 /* this is called from the webrtc thread with the pc lock held */
1607 static void
1608 _on_data_channel_ready_state (GstWebRTCDataChannel * channel,
1609     GParamSpec * pspec, GstWebRTCBin * webrtc)
1610 {
1611   GstWebRTCDataChannelState ready_state;
1612   guint i;
1613
1614   g_object_get (channel, "ready-state", &ready_state, NULL);
1615
1616   if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
1617     gboolean found = FALSE;
1618
1619     for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) {
1620       GstWebRTCDataChannel *c;
1621
1622       c = g_array_index (webrtc->priv->pending_data_channels,
1623           GstWebRTCDataChannel *, i);
1624       if (c == channel) {
1625         found = TRUE;
1626         g_array_remove_index (webrtc->priv->pending_data_channels, i);
1627         break;
1628       }
1629     }
1630     if (found == FALSE) {
1631       GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
1632       return;
1633     }
1634
1635     g_array_append_val (webrtc->priv->data_channels, channel);
1636
1637     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
1638         gst_object_ref (channel));
1639   }
1640 }
1641
1642 static void
1643 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
1644     GstWebRTCBin * webrtc)
1645 {
1646   GstWebRTCDataChannel *channel;
1647   guint stream_id;
1648   GstPad *sink_pad;
1649
1650   if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
1651     return;
1652
1653   PC_LOCK (webrtc);
1654   channel = _find_data_channel_for_id (webrtc, stream_id);
1655   if (!channel) {
1656     channel = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, NULL);
1657     channel->id = stream_id;
1658     channel->webrtcbin = webrtc;
1659
1660     gst_bin_add (GST_BIN (webrtc), channel->appsrc);
1661     gst_bin_add (GST_BIN (webrtc), channel->appsink);
1662
1663     gst_element_sync_state_with_parent (channel->appsrc);
1664     gst_element_sync_state_with_parent (channel->appsink);
1665
1666     gst_webrtc_data_channel_link_to_sctp (channel,
1667         webrtc->priv->sctp_transport);
1668
1669     g_array_append_val (webrtc->priv->pending_data_channels, channel);
1670   }
1671
1672   g_signal_connect (channel, "notify::ready-state",
1673       G_CALLBACK (_on_data_channel_ready_state), webrtc);
1674
1675   sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
1676   if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
1677     GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
1678         GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
1679   gst_object_unref (sink_pad);
1680   PC_UNLOCK (webrtc);
1681 }
1682
1683 static void
1684 _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
1685     GstWebRTCBin * webrtc)
1686 {
1687   GstWebRTCSCTPTransportState state;
1688
1689   g_object_get (sctp, "state", &state, NULL);
1690
1691   if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
1692     int i;
1693
1694     PC_LOCK (webrtc);
1695     GST_DEBUG_OBJECT (webrtc, "SCTP association established");
1696
1697     for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1698       GstWebRTCDataChannel *channel;
1699
1700       channel =
1701           g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
1702           i);
1703
1704       gst_webrtc_data_channel_link_to_sctp (channel,
1705           webrtc->priv->sctp_transport);
1706
1707       if (!channel->negotiated && !channel->opened)
1708         gst_webrtc_data_channel_start_negotiation (channel);
1709     }
1710     PC_UNLOCK (webrtc);
1711   }
1712 }
1713
1714 static TransportStream *
1715 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
1716 {
1717   if (!webrtc->priv->data_channel_transport) {
1718     TransportStream *stream;
1719     GstWebRTCSCTPTransport *sctp_transport;
1720     int i;
1721
1722     stream = _find_transport_for_session (webrtc, session_id);
1723
1724     if (!stream) {
1725       stream = _create_transport_channel (webrtc, session_id);
1726       gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
1727       gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
1728       g_array_append_val (webrtc->priv->transports, stream);
1729     }
1730
1731     webrtc->priv->data_channel_transport = stream;
1732
1733     g_object_set (stream, "rtcp-mux", TRUE, NULL);
1734
1735     if (!(sctp_transport = webrtc->priv->sctp_transport)) {
1736       sctp_transport = gst_webrtc_sctp_transport_new ();
1737       sctp_transport->transport =
1738           g_object_ref (webrtc->priv->data_channel_transport->transport);
1739       sctp_transport->webrtcbin = webrtc;
1740
1741       gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
1742       gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
1743     }
1744
1745     g_signal_connect (sctp_transport->sctpdec, "pad-added",
1746         G_CALLBACK (_on_sctpdec_pad_added), webrtc);
1747     g_signal_connect (sctp_transport, "notify::state",
1748         G_CALLBACK (_on_sctp_state_notify), webrtc);
1749
1750     if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
1751             GST_ELEMENT (sctp_transport->sctpdec), "sink"))
1752       g_warn_if_reached ();
1753
1754     if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
1755             GST_ELEMENT (stream->send_bin), "data_sink"))
1756       g_warn_if_reached ();
1757
1758     for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1759       GstWebRTCDataChannel *channel;
1760
1761       channel =
1762           g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
1763           i);
1764
1765       gst_webrtc_data_channel_link_to_sctp (channel,
1766           webrtc->priv->sctp_transport);
1767     }
1768
1769     gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
1770     gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
1771
1772     if (!webrtc->priv->sctp_transport) {
1773       gst_element_sync_state_with_parent (GST_ELEMENT
1774           (sctp_transport->sctpdec));
1775       gst_element_sync_state_with_parent (GST_ELEMENT
1776           (sctp_transport->sctpenc));
1777     }
1778
1779     webrtc->priv->sctp_transport = sctp_transport;
1780   }
1781
1782   return webrtc->priv->data_channel_transport;
1783 }
1784
1785 static TransportStream *
1786 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
1787     gboolean is_datachannel)
1788 {
1789   if (is_datachannel)
1790     return _get_or_create_data_channel_transports (webrtc, session_id);
1791   else
1792     return _get_or_create_rtp_transport_channel (webrtc, session_id);
1793 }
1794
1795 static guint
1796 g_array_find_uint (GArray * array, guint val)
1797 {
1798   guint i;
1799
1800   for (i = 0; i < array->len; i++) {
1801     if (g_array_index (array, guint, i) == val)
1802       return i;
1803   }
1804
1805   return G_MAXUINT;
1806 }
1807
1808 static gboolean
1809 _pick_available_pt (GArray * reserved_pts, guint * i)
1810 {
1811   gboolean ret = FALSE;
1812
1813   for (*i = 96; *i <= 127; (*i)++) {
1814     if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
1815       g_array_append_val (reserved_pts, *i);
1816       ret = TRUE;
1817       break;
1818     }
1819   }
1820
1821   return ret;
1822 }
1823
1824 static gboolean
1825 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
1826     GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
1827     GstSDPMedia * media)
1828 {
1829   gboolean ret = TRUE;
1830
1831   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
1832     goto done;
1833
1834   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
1835     guint pt;
1836     gchar *str;
1837
1838     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1839       goto done;
1840
1841     /* https://tools.ietf.org/html/rfc5109#section-14.1 */
1842
1843     str = g_strdup_printf ("%u", pt);
1844     gst_sdp_media_add_format (media, str);
1845     g_free (str);
1846     str = g_strdup_printf ("%u red/%d", pt, clockrate);
1847     gst_sdp_media_add_attribute (media, "rtpmap", str);
1848     g_free (str);
1849
1850     *rtx_target_pt = pt;
1851
1852     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1853       goto done;
1854
1855     str = g_strdup_printf ("%u", pt);
1856     gst_sdp_media_add_format (media, str);
1857     g_free (str);
1858     str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
1859     gst_sdp_media_add_attribute (media, "rtpmap", str);
1860     g_free (str);
1861   }
1862
1863 done:
1864   return ret;
1865 }
1866
1867 static gboolean
1868 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
1869     GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
1870     GstSDPMedia * media)
1871 {
1872   gboolean ret = TRUE;
1873
1874   if (trans->local_rtx_ssrc_map)
1875     gst_structure_free (trans->local_rtx_ssrc_map);
1876
1877   trans->local_rtx_ssrc_map =
1878       gst_structure_new_empty ("application/x-rtp-ssrc-map");
1879
1880   if (trans->do_nack) {
1881     guint pt;
1882     gchar *str;
1883
1884     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1885       goto done;
1886
1887     /* https://tools.ietf.org/html/rfc4588#section-8.6 */
1888
1889     str = g_strdup_printf ("%u", target_ssrc);
1890     gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
1891         g_random_int (), NULL);
1892     g_free (str);
1893
1894     str = g_strdup_printf ("%u", pt);
1895     gst_sdp_media_add_format (media, str);
1896     g_free (str);
1897
1898     str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
1899     gst_sdp_media_add_attribute (media, "rtpmap", str);
1900     g_free (str);
1901
1902     str = g_strdup_printf ("%u apt=%d", pt, target_pt);
1903     gst_sdp_media_add_attribute (media, "fmtp", str);
1904     g_free (str);
1905   }
1906
1907 done:
1908   return ret;
1909 }
1910
1911 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
1912 static gboolean
1913 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
1914     GstSDPMedia * media)
1915 {
1916   gchar *str;
1917
1918   str =
1919       g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
1920       g_value_get_uint (value));
1921   gst_sdp_media_add_attribute (media, "ssrc-group", str);
1922
1923   g_free (str);
1924
1925   return TRUE;
1926 }
1927
1928 typedef struct
1929 {
1930   GstSDPMedia *media;
1931   GstWebRTCBin *webrtc;
1932   WebRTCTransceiver *trans;
1933 } RtxSsrcData;
1934
1935 static gboolean
1936 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
1937 {
1938   gchar *str;
1939   GstStructure *sdes;
1940   const gchar *cname;
1941
1942   g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
1943   /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
1944   cname = gst_structure_get_string (sdes, "cname");
1945
1946   /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
1947   str =
1948       g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
1949       cname, GST_OBJECT_NAME (data->trans));
1950   gst_sdp_media_add_attribute (data->media, "ssrc", str);
1951   g_free (str);
1952
1953   str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
1954   gst_sdp_media_add_attribute (data->media, "ssrc", str);
1955   g_free (str);
1956
1957   gst_structure_free (sdes);
1958
1959   return TRUE;
1960 }
1961
1962 static void
1963 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
1964     WebRTCTransceiver * trans)
1965 {
1966   guint i;
1967   RtxSsrcData data = { media, webrtc, trans };
1968   const gchar *cname;
1969   GstStructure *sdes;
1970
1971   g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
1972   /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
1973   cname = gst_structure_get_string (sdes, "cname");
1974
1975   if (trans->local_rtx_ssrc_map)
1976     gst_structure_foreach (trans->local_rtx_ssrc_map,
1977         (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
1978
1979   for (i = 0; i < gst_caps_get_size (caps); i++) {
1980     const GstStructure *s = gst_caps_get_structure (caps, i);
1981     guint ssrc;
1982
1983     if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1984       gchar *str;
1985
1986       /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
1987       str =
1988           g_strdup_printf ("%u msid:%s %s", ssrc, cname,
1989           GST_OBJECT_NAME (trans));
1990       gst_sdp_media_add_attribute (media, "ssrc", str);
1991       g_free (str);
1992
1993       str = g_strdup_printf ("%u cname:%s", ssrc, cname);
1994       gst_sdp_media_add_attribute (media, "ssrc", str);
1995       g_free (str);
1996     }
1997   }
1998
1999   gst_structure_free (sdes);
2000
2001   if (trans->local_rtx_ssrc_map)
2002     gst_structure_foreach (trans->local_rtx_ssrc_map,
2003         (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
2004 }
2005
2006 static void
2007 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
2008     GstSDPMedia * media)
2009 {
2010   gchar *cert, *fingerprint, *val;
2011
2012   g_object_get (transport, "certificate", &cert, NULL);
2013
2014   fingerprint =
2015       _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
2016   g_free (cert);
2017   val =
2018       g_strdup_printf ("%s %s",
2019       _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
2020   g_free (fingerprint);
2021
2022   gst_sdp_media_add_attribute (media, "fingerprint", val);
2023   g_free (val);
2024 }
2025
2026 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
2027 static gboolean
2028 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
2029     GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
2030     GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag,
2031     gchar * bundle_pwd, GArray * reserved_pts)
2032 {
2033   /* TODO:
2034    * rtp header extensions
2035    * ice attributes
2036    * rtx
2037    * fec
2038    * msid-semantics
2039    * msid
2040    * dtls fingerprints
2041    * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
2042    */
2043   GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2044   gchar *direction, *sdp_mid, *ufrag, *pwd;
2045   gboolean bundle_only;
2046   GstCaps *caps;
2047   int i;
2048
2049   if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2050       || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
2051     return FALSE;
2052
2053   g_assert (trans->mline == -1 || trans->mline == media_idx);
2054
2055   bundle_only = bundled_mids && bundle_idx != media_idx
2056       && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE;
2057
2058   /* mandated by JSEP */
2059   gst_sdp_media_add_attribute (media, "setup", "actpass");
2060
2061   /* FIXME: deal with ICE restarts */
2062   if (last_offer && trans->mline != -1 && trans->mid) {
2063     ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline));
2064     pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline));
2065     GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx);
2066   } else {
2067     GST_DEBUG_OBJECT (trans,
2068         "%u Generating new ice parameters mline %i, mid %s", media_idx,
2069         trans->mline, trans->mid);
2070     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2071       _generate_ice_credentials (&ufrag, &pwd);
2072     } else {
2073       g_assert (bundle_ufrag && bundle_pwd);
2074       ufrag = g_strdup (bundle_ufrag);
2075       pwd = g_strdup (bundle_pwd);
2076     }
2077   }
2078
2079   gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2080   gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2081   g_free (ufrag);
2082   g_free (pwd);
2083
2084   gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0);
2085   gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2086   gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2087
2088   if (bundle_only) {
2089     gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2090   }
2091
2092   /* FIXME: negotiate this */
2093   /* FIXME: when bundle_only, these should not be added:
2094    * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
2095    * However, this causes incompatibilities with current versions
2096    * of the major browsers */
2097   gst_sdp_media_add_attribute (media, "rtcp-mux", "");
2098   gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
2099
2100   direction =
2101       _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
2102       trans->direction);
2103   gst_sdp_media_add_attribute (media, direction, "");
2104   g_free (direction);
2105
2106   if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2107     caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
2108     caps =
2109         _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
2110         caps);
2111   } else {
2112     g_assert_not_reached ();
2113   }
2114
2115   if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2116     GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2117     if (caps)
2118       gst_caps_unref (caps);
2119     return FALSE;
2120   }
2121
2122   for (i = 0; i < gst_caps_get_size (caps); i++) {
2123     GstCaps *format = gst_caps_new_empty ();
2124     const GstStructure *s = gst_caps_get_structure (caps, i);
2125
2126     gst_caps_append_structure (format, gst_structure_copy (s));
2127
2128     GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
2129         " to %u-th media", i, format, media_idx);
2130
2131     /* this only looks at the first structure so we loop over the given caps
2132      * and add each structure inside it piecemeal */
2133     gst_sdp_media_set_media_from_caps (format, media);
2134
2135     gst_caps_unref (format);
2136   }
2137
2138   if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2139     const GstStructure *s = gst_caps_get_structure (caps, 0);
2140     gint clockrate = -1;
2141     gint rtx_target_pt;
2142     gint original_rtx_target_pt;        /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2143     guint rtx_target_ssrc = -1;
2144
2145     if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
2146         webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2147       g_array_append_val (reserved_pts, rtx_target_pt);
2148
2149     original_rtx_target_pt = rtx_target_pt;
2150
2151     if (!gst_structure_get_int (s, "clock-rate", &clockrate))
2152       GST_WARNING_OBJECT (webrtc,
2153           "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
2154     if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
2155       GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
2156           caps);
2157
2158     _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2159         clockrate, &rtx_target_pt, media);
2160     _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2161         clockrate, rtx_target_pt, rtx_target_ssrc, media);
2162     if (original_rtx_target_pt != rtx_target_pt)
2163       _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2164           clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
2165   }
2166
2167   _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
2168
2169   /* Some identifier; we also add the media name to it so it's identifiable */
2170   if (trans->mid) {
2171     gst_sdp_media_add_attribute (media, "mid", trans->mid);
2172   } else {
2173     sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2174         webrtc->priv->media_counter++);
2175     gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2176     g_free (sdp_mid);
2177   }
2178
2179   /* TODO:
2180    * - add a=candidate lines for gathered candidates
2181    */
2182
2183   if (trans->sender) {
2184     if (!trans->sender->transport) {
2185       TransportStream *item;
2186
2187       item =
2188           _get_or_create_transport_stream (webrtc,
2189           bundled_mids ? bundle_idx : media_idx, FALSE);
2190
2191       webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
2192     }
2193
2194     _add_fingerprint_to_media (trans->sender->transport, media);
2195   }
2196
2197   if (bundled_mids) {
2198     const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2199
2200     g_assert (mid);
2201     g_string_append_printf (bundled_mids, " %s", mid);
2202   }
2203
2204   gst_caps_unref (caps);
2205
2206   return TRUE;
2207 }
2208
2209 static void
2210 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
2211 {
2212   if (pad->received_caps) {
2213     GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
2214     gint pt;
2215
2216     if (gst_structure_get_int (s, "payload", &pt)) {
2217       g_array_append_val (reserved_pts, pt);
2218     }
2219   }
2220 }
2221
2222 static GArray *
2223 gather_reserved_pts (GstWebRTCBin * webrtc)
2224 {
2225   GstElement *element = GST_ELEMENT (webrtc);
2226   GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2227
2228   GST_OBJECT_LOCK (webrtc);
2229   g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
2230   g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
2231       reserved_pts);
2232   GST_OBJECT_UNLOCK (webrtc);
2233
2234   return reserved_pts;
2235 }
2236
2237 static gboolean
2238 _add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg,
2239     GstSDPMedia * media, GString * bundled_mids, guint bundle_idx,
2240     gchar * bundle_ufrag, gchar * bundle_pwd)
2241 {
2242   GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2243   gchar *ufrag, *pwd, *sdp_mid;
2244   gboolean bundle_only = bundled_mids
2245       && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2246       && gst_sdp_message_medias_len (msg) != bundle_idx;
2247   guint last_data_index = G_MAXUINT;
2248
2249   /* add data channel support */
2250   if (webrtc->priv->data_channels->len == 0)
2251     return FALSE;
2252
2253   if (last_offer) {
2254     last_data_index = _message_get_datachannel_index (last_offer);
2255     if (last_data_index < G_MAXUINT) {
2256       g_assert (last_data_index < gst_sdp_message_medias_len (last_offer));
2257       /* XXX: is this always true when recycling transceivers?
2258        * i.e. do we always put the data channel in the same mline */
2259       g_assert (last_data_index == gst_sdp_message_medias_len (msg));
2260     }
2261   }
2262
2263   /* mandated by JSEP */
2264   gst_sdp_media_add_attribute (media, "setup", "actpass");
2265
2266   /* FIXME: only needed when restarting ICE */
2267   if (last_offer && last_data_index < G_MAXUINT) {
2268     ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index));
2269     pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index));
2270   } else {
2271     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2272       _generate_ice_credentials (&ufrag, &pwd);
2273     } else {
2274       ufrag = g_strdup (bundle_ufrag);
2275       pwd = g_strdup (bundle_pwd);
2276     }
2277   }
2278   gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2279   gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2280   g_free (ufrag);
2281   g_free (pwd);
2282
2283   gst_sdp_media_set_media (media, "application");
2284   gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
2285   gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2286   gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2287   gst_sdp_media_add_format (media, "webrtc-datachannel");
2288
2289   if (bundle_idx != gst_sdp_message_medias_len (msg))
2290     gst_sdp_media_add_attribute (media, "bundle-only", NULL);
2291
2292   if (last_offer && last_data_index < G_MAXUINT) {
2293     const GstSDPMedia *last_data_media;
2294     const gchar *mid;
2295
2296     last_data_media = gst_sdp_message_get_media (last_offer, last_data_index);
2297     mid = gst_sdp_media_get_attribute_val (last_data_media, "mid");
2298
2299     gst_sdp_media_add_attribute (media, "mid", mid);
2300   } else {
2301     sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2302         webrtc->priv->media_counter++);
2303     gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2304     g_free (sdp_mid);
2305   }
2306
2307   if (bundled_mids) {
2308     const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2309
2310     g_assert (mid);
2311     g_string_append_printf (bundled_mids, " %s", mid);
2312   }
2313
2314   /* FIXME: negotiate this properly */
2315   gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2316
2317   _get_or_create_data_channel_transports (webrtc,
2318       bundled_mids ? 0 : webrtc->priv->transceivers->len);
2319   _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media);
2320
2321   return TRUE;
2322 }
2323
2324 /* TODO: use the options argument */
2325 static GstSDPMessage *
2326 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
2327 {
2328   GstSDPMessage *ret;
2329   GString *bundled_mids = NULL;
2330   gchar *bundle_ufrag = NULL;
2331   gchar *bundle_pwd = NULL;
2332   GArray *reserved_pts = NULL;
2333   GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc);
2334   GList *seen_transceivers = NULL;
2335   guint media_idx = 0;
2336   int i;
2337
2338   gst_sdp_message_new (&ret);
2339
2340   gst_sdp_message_set_version (ret, "0");
2341   {
2342     gchar *v, *sess_id;
2343     v = g_strdup_printf ("%u", webrtc->priv->offer_count++);
2344     if (last_offer) {
2345       const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer);
2346       sess_id = g_strdup (origin->sess_id);
2347     } else {
2348       sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
2349     }
2350     gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0");
2351     g_free (sess_id);
2352     g_free (v);
2353   }
2354   gst_sdp_message_set_session_name (ret, "-");
2355   gst_sdp_message_add_time (ret, "0", "0", NULL);
2356   gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
2357
2358   if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
2359     bundled_mids = g_string_new ("BUNDLE");
2360   } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
2361     bundled_mids = g_string_new ("BUNDLE");
2362   }
2363
2364   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2365     GStrv last_bundle = NULL;
2366     guint bundle_media_index;
2367
2368     reserved_pts = gather_reserved_pts (webrtc);
2369     if (last_offer && _parse_bundle (last_offer, &last_bundle) && last_bundle
2370         && last_bundle && last_bundle[0]
2371         && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) {
2372       bundle_ufrag =
2373           g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index));
2374       bundle_pwd =
2375           g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index));
2376     } else {
2377       _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2378     }
2379
2380     g_strfreev (last_bundle);
2381   }
2382
2383   /* FIXME: recycle transceivers */
2384
2385   /* Fill up the renegotiated streams first */
2386   if (last_offer) {
2387     for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) {
2388       GstWebRTCRTPTransceiver *trans = NULL;
2389       const GstSDPMedia *last_media;
2390
2391       last_media = gst_sdp_message_get_media (last_offer, i);
2392
2393       if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0
2394           || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) {
2395         const gchar *last_mid;
2396         int j;
2397         last_mid = gst_sdp_media_get_attribute_val (last_media, "mid");
2398
2399         for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2400           trans =
2401               g_array_index (webrtc->priv->transceivers,
2402               GstWebRTCRTPTransceiver *, j);
2403
2404           if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) {
2405             GstSDPMedia *media;
2406
2407             g_assert (!g_list_find (seen_transceivers, trans));
2408
2409             GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %"
2410                 GST_PTR_FORMAT " with mid %s into media index %u", trans,
2411                 trans->mid, media_idx);
2412
2413             /* FIXME: deal with format changes */
2414             gst_sdp_media_copy (last_media, &media);
2415             _media_replace_direction (media, trans->direction);
2416
2417             if (bundled_mids) {
2418               const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2419
2420               g_assert (mid);
2421               g_string_append_printf (bundled_mids, " %s", mid);
2422             }
2423
2424             gst_sdp_message_add_media (ret, media);
2425             media_idx++;
2426
2427             gst_sdp_media_free (media);
2428             seen_transceivers = g_list_prepend (seen_transceivers, trans);
2429             break;
2430           }
2431         }
2432       } else if (g_strcmp0 (gst_sdp_media_get_media (last_media),
2433               "application") == 0) {
2434         GstSDPMedia media = { 0, };
2435         gst_sdp_media_init (&media);
2436         if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
2437                 bundle_ufrag, bundle_pwd)) {
2438           gst_sdp_message_add_media (ret, &media);
2439           media_idx++;
2440         } else {
2441           gst_sdp_media_uninit (&media);
2442         }
2443       }
2444     }
2445   }
2446
2447   /* add any extra streams */
2448   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2449     GstWebRTCRTPTransceiver *trans;
2450     GstSDPMedia media = { 0, };
2451
2452     trans =
2453         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
2454         i);
2455
2456     /* don't add transceivers twice */
2457     if (g_list_find (seen_transceivers, trans))
2458       continue;
2459
2460     /* don't add stopped transceivers */
2461     if (trans->stopped)
2462       continue;
2463
2464     gst_sdp_media_init (&media);
2465
2466     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2467       reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2468     }
2469
2470     GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media "
2471         "index %u", trans, media_idx);
2472
2473     if (sdp_media_from_transceiver (webrtc, &media, trans,
2474             GST_WEBRTC_SDP_TYPE_OFFER, media_idx, bundled_mids, 0, bundle_ufrag,
2475             bundle_pwd, reserved_pts)) {
2476       gst_sdp_message_add_media (ret, &media);
2477       media_idx++;
2478     } else {
2479       gst_sdp_media_uninit (&media);
2480     }
2481
2482     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2483       g_array_free (reserved_pts, TRUE);
2484     }
2485     seen_transceivers = g_list_prepend (seen_transceivers, trans);
2486   }
2487
2488   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2489     g_array_free (reserved_pts, TRUE);
2490   }
2491
2492   /* add a data channel if exists and not renegotiated */
2493   if (_message_get_datachannel_index (ret) == G_MAXUINT) {
2494     GstSDPMedia media = { 0, };
2495     gst_sdp_media_init (&media);
2496     if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0,
2497             bundle_ufrag, bundle_pwd)) {
2498       gst_sdp_message_add_media (ret, &media);
2499       media_idx++;
2500     } else {
2501       gst_sdp_media_uninit (&media);
2502     }
2503   }
2504
2505   g_assert (media_idx == gst_sdp_message_medias_len (ret));
2506
2507   if (bundled_mids) {
2508     gchar *mids = g_string_free (bundled_mids, FALSE);
2509
2510     gst_sdp_message_add_attribute (ret, "group", mids);
2511     g_free (mids);
2512   }
2513
2514   if (bundle_ufrag)
2515     g_free (bundle_ufrag);
2516
2517   if (bundle_pwd)
2518     g_free (bundle_pwd);
2519
2520   /* FIXME: pre-emptively setup receiving elements when needed */
2521
2522   /* XXX: only true for the initial offerer */
2523   g_object_set (webrtc->priv->ice, "controller", TRUE, NULL);
2524
2525   g_list_free (seen_transceivers);
2526
2527   if (webrtc->priv->last_generated_answer)
2528     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
2529   webrtc->priv->last_generated_answer = NULL;
2530   if (webrtc->priv->last_generated_offer)
2531     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
2532   {
2533     GstSDPMessage *copy;
2534     gst_sdp_message_copy (ret, &copy);
2535     webrtc->priv->last_generated_offer =
2536         gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy);
2537   }
2538
2539   return ret;
2540 }
2541
2542 static void
2543 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
2544     gint * rtx_target_pt)
2545 {
2546   guint i;
2547
2548   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2549     return;
2550
2551   for (i = 0; i < gst_caps_get_size (caps); i++) {
2552     const GstStructure *s = gst_caps_get_structure (caps, i);
2553
2554     if (gst_structure_has_name (s, "application/x-rtp")) {
2555       const gchar *encoding_name =
2556           gst_structure_get_string (s, "encoding-name");
2557       gint clock_rate;
2558       gint pt;
2559
2560       if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
2561           gst_structure_get_int (s, "payload", &pt)) {
2562         if (!g_strcmp0 (encoding_name, "RED")) {
2563           gchar *str;
2564
2565           str = g_strdup_printf ("%u", pt);
2566           gst_sdp_media_add_format (media, str);
2567           g_free (str);
2568           str = g_strdup_printf ("%u red/%d", pt, clock_rate);
2569           *rtx_target_pt = pt;
2570           gst_sdp_media_add_attribute (media, "rtpmap", str);
2571           g_free (str);
2572         } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
2573           gchar *str;
2574
2575           str = g_strdup_printf ("%u", pt);
2576           gst_sdp_media_add_format (media, str);
2577           g_free (str);
2578           str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
2579           gst_sdp_media_add_attribute (media, "rtpmap", str);
2580           g_free (str);
2581         }
2582       }
2583     }
2584   }
2585 }
2586
2587 static void
2588 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
2589     GstCaps * offer_caps, gint target_pt, guint target_ssrc)
2590 {
2591   guint i;
2592   const GstStructure *s;
2593
2594   if (trans->local_rtx_ssrc_map)
2595     gst_structure_free (trans->local_rtx_ssrc_map);
2596
2597   trans->local_rtx_ssrc_map =
2598       gst_structure_new_empty ("application/x-rtp-ssrc-map");
2599
2600   for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
2601     s = gst_caps_get_structure (offer_caps, i);
2602
2603     if (gst_structure_has_name (s, "application/x-rtp")) {
2604       const gchar *encoding_name =
2605           gst_structure_get_string (s, "encoding-name");
2606       const gchar *apt_str = gst_structure_get_string (s, "apt");
2607       gint apt;
2608       gint clock_rate;
2609       gint pt;
2610
2611       if (!apt_str)
2612         continue;
2613
2614       apt = atoi (apt_str);
2615
2616       if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
2617           gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
2618         if (!g_strcmp0 (encoding_name, "RTX")) {
2619           gchar *str;
2620
2621           str = g_strdup_printf ("%u", pt);
2622           gst_sdp_media_add_format (media, str);
2623           g_free (str);
2624           str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
2625           gst_sdp_media_add_attribute (media, "rtpmap", str);
2626           g_free (str);
2627
2628           str = g_strdup_printf ("%d apt=%d", pt, apt);
2629           gst_sdp_media_add_attribute (media, "fmtp", str);
2630           g_free (str);
2631
2632           str = g_strdup_printf ("%u", target_ssrc);
2633           gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2634               g_random_int (), NULL);
2635         }
2636       }
2637     }
2638   }
2639 }
2640
2641 static void
2642 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
2643     guint * target_ssrc)
2644 {
2645   const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
2646
2647   gst_structure_get_int (s, "payload", target_pt);
2648   gst_structure_get_uint (s, "ssrc", target_ssrc);
2649 }
2650
2651 /* TODO: use the options argument */
2652 static GstSDPMessage *
2653 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
2654 {
2655   GstSDPMessage *ret = NULL;
2656   const GstWebRTCSessionDescription *pending_remote =
2657       webrtc->pending_remote_description;
2658   guint i;
2659   GStrv bundled = NULL;
2660   guint bundle_idx = 0;
2661   GString *bundled_mids = NULL;
2662   gchar *bundle_ufrag = NULL;
2663   gchar *bundle_pwd = NULL;
2664   GList *seen_transceivers = NULL;
2665   GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc);
2666
2667   if (!webrtc->pending_remote_description) {
2668     GST_ERROR_OBJECT (webrtc,
2669         "Asked to create an answer without a remote description");
2670     return NULL;
2671   }
2672
2673   if (!_parse_bundle (pending_remote->sdp, &bundled))
2674     goto out;
2675
2676   if (bundled) {
2677     GStrv last_bundle = NULL;
2678     guint bundle_media_index;
2679
2680     if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
2681       GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
2682           bundled[0]);
2683       goto out;
2684     }
2685
2686     if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2687       bundled_mids = g_string_new ("BUNDLE");
2688     }
2689
2690     if (last_answer && _parse_bundle (last_answer, &last_bundle)
2691         && last_bundle && last_bundle[0]
2692         && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) {
2693       bundle_ufrag =
2694           g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index));
2695       bundle_pwd =
2696           g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index));
2697     } else {
2698       _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2699     }
2700
2701     g_strfreev (last_bundle);
2702   }
2703
2704   gst_sdp_message_new (&ret);
2705
2706   gst_sdp_message_set_version (ret, "0");
2707   {
2708     const GstSDPOrigin *offer_origin =
2709         gst_sdp_message_get_origin (pending_remote->sdp);
2710     gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id,
2711         offer_origin->sess_version, "IN", "IP4", "0.0.0.0");
2712   }
2713   gst_sdp_message_set_session_name (ret, "-");
2714
2715   for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
2716     const GstSDPAttribute *attr =
2717         gst_sdp_message_get_attribute (pending_remote->sdp, i);
2718
2719     if (g_strcmp0 (attr->key, "ice-options") == 0) {
2720       gst_sdp_message_add_attribute (ret, attr->key, attr->value);
2721     }
2722   }
2723
2724   for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
2725     GstSDPMedia *media = NULL;
2726     GstSDPMedia *offer_media;
2727     GstWebRTCDTLSSetup offer_setup, answer_setup;
2728     guint j, k;
2729     gboolean bundle_only;
2730     const gchar *mid;
2731
2732     offer_media =
2733         (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
2734     bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
2735
2736     gst_sdp_media_new (&media);
2737     if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2738       gst_sdp_media_set_port_info (media, 0, 0);
2739     else
2740       gst_sdp_media_set_port_info (media, 9, 0);
2741     gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2742
2743     {
2744       gchar *ufrag, *pwd;
2745
2746       /* FIXME: deal with ICE restarts */
2747       if (last_answer && i < gst_sdp_message_medias_len (last_answer)) {
2748         ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i));
2749         pwd = g_strdup (_media_get_ice_pwd (last_answer, i));
2750       } else {
2751         if (!bundled) {
2752           _generate_ice_credentials (&ufrag, &pwd);
2753         } else {
2754           ufrag = g_strdup (bundle_ufrag);
2755           pwd = g_strdup (bundle_pwd);
2756         }
2757       }
2758       gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2759       gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2760       g_free (ufrag);
2761       g_free (pwd);
2762     }
2763
2764     for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
2765       const GstSDPAttribute *attr =
2766           gst_sdp_media_get_attribute (offer_media, j);
2767
2768       if (g_strcmp0 (attr->key, "mid") == 0
2769           || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
2770         gst_sdp_media_add_attribute (media, attr->key, attr->value);
2771         /* FIXME: handle anything we want to keep */
2772       }
2773     }
2774
2775     mid = gst_sdp_media_get_attribute_val (media, "mid");
2776     /* XXX: not strictly required but a lot of functionality requires a mid */
2777     g_assert (mid);
2778
2779     /* set the a=setup: attribute */
2780     offer_setup = _get_dtls_setup_from_media (offer_media);
2781     answer_setup = _intersect_dtls_setup (offer_setup);
2782     if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
2783       GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
2784           "transceiver direction");
2785       goto rejected;
2786     }
2787     _media_replace_setup (media, answer_setup);
2788
2789     if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
2790       int sctp_port;
2791
2792       if (gst_sdp_media_formats_len (offer_media) != 1) {
2793         GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
2794             "for webrtc-datachannel");
2795         goto rejected;
2796       }
2797       sctp_port = _get_sctp_port_from_media (offer_media);
2798       if (sctp_port == -1) {
2799         GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
2800         goto rejected;
2801       }
2802
2803       /* XXX: older browsers will produce a different SDP format for data
2804        * channel that is currently not parsed correctly */
2805       gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2806
2807       gst_sdp_media_set_media (media, "application");
2808       gst_sdp_media_set_port_info (media, 9, 0);
2809       gst_sdp_media_add_format (media, "webrtc-datachannel");
2810
2811       /* FIXME: negotiate this properly on renegotiation */
2812       gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2813
2814       _get_or_create_data_channel_transports (webrtc,
2815           bundled_mids ? bundle_idx : i);
2816
2817       if (bundled_mids) {
2818         g_assert (mid);
2819         g_string_append_printf (bundled_mids, " %s", mid);
2820       }
2821
2822       _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
2823           media);
2824     } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
2825         || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
2826       GstCaps *offer_caps, *answer_caps = NULL;
2827       GstWebRTCRTPTransceiver *rtp_trans = NULL;
2828       WebRTCTransceiver *trans = NULL;
2829       GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
2830       gint target_pt = -1;
2831       gint original_target_pt = -1;
2832       guint target_ssrc = 0;
2833
2834       gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2835       offer_caps = _rtp_caps_from_media (offer_media);
2836
2837       if (last_answer && i < gst_sdp_message_medias_len (last_answer)
2838           && (rtp_trans =
2839               _find_transceiver (webrtc, mid,
2840                   (FindTransceiverFunc) match_for_mid))) {
2841         const GstSDPMedia *last_media =
2842             gst_sdp_message_get_media (last_answer, i);
2843         const gchar *last_mid =
2844             gst_sdp_media_get_attribute_val (last_media, "mid");
2845
2846         /* FIXME: assumes no shenanigans with recycling transceivers */
2847         g_assert (g_strcmp0 (mid, last_mid) == 0);
2848
2849         if (!answer_caps
2850             && (rtp_trans->direction ==
2851                 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
2852                 || rtp_trans->direction ==
2853                 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY))
2854           answer_caps =
2855               _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
2856         if (!answer_caps
2857             && (rtp_trans->direction ==
2858                 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV
2859                 || rtp_trans->direction ==
2860                 GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY))
2861           answer_caps =
2862               _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SRC, i);
2863         if (!answer_caps)
2864           answer_caps = _rtp_caps_from_media (last_media);
2865
2866         /* XXX: In theory we're meant to use the sendrecv formats for the
2867          * inactive direction however we don't know what that may be and would
2868          * require asking outside what it expects to possibly send later */
2869
2870         GST_LOG_OBJECT (webrtc, "Found existing previously negotiated "
2871             "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u "
2872             "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps);
2873       } else {
2874         for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2875           GstCaps *trans_caps;
2876
2877           rtp_trans =
2878               g_array_index (webrtc->priv->transceivers,
2879               GstWebRTCRTPTransceiver *, j);
2880
2881           if (g_list_find (seen_transceivers, rtp_trans)) {
2882             /* Don't double allocate a transceiver to multiple mlines */
2883             rtp_trans = NULL;
2884             continue;
2885           }
2886
2887           trans_caps =
2888               _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
2889
2890           GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
2891               " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
2892
2893           /* FIXME: technically this is a little overreaching as some fields we
2894            * we can deal with not having and/or we may have unrecognized fields
2895            * that we cannot actually support */
2896           if (trans_caps) {
2897             answer_caps = gst_caps_intersect (offer_caps, trans_caps);
2898             if (answer_caps && !gst_caps_is_empty (answer_caps)) {
2899               GST_LOG_OBJECT (webrtc,
2900                   "found compatible transceiver %" GST_PTR_FORMAT
2901                   " for offer media %u", rtp_trans, i);
2902               if (trans_caps)
2903                 gst_caps_unref (trans_caps);
2904               break;
2905             } else {
2906               if (answer_caps) {
2907                 gst_caps_unref (answer_caps);
2908                 answer_caps = NULL;
2909               }
2910               if (trans_caps)
2911                 gst_caps_unref (trans_caps);
2912               rtp_trans = NULL;
2913             }
2914           } else {
2915             rtp_trans = NULL;
2916           }
2917         }
2918       }
2919
2920       if (rtp_trans) {
2921         answer_dir = rtp_trans->direction;
2922         g_assert (answer_caps != NULL);
2923       } else {
2924         /* if no transceiver, then we only receive that stream and respond with
2925          * the exact same caps */
2926         /* FIXME: how to validate that subsequent elements can actually receive
2927          * this payload/format */
2928         answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
2929         answer_caps = gst_caps_ref (offer_caps);
2930       }
2931
2932       if (gst_caps_is_empty (answer_caps)) {
2933         GST_WARNING_OBJECT (webrtc, "Could not create caps for media");
2934         if (rtp_trans)
2935           gst_object_unref (rtp_trans);
2936         gst_caps_unref (answer_caps);
2937         goto rejected;
2938       }
2939
2940       seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans);
2941
2942       if (!rtp_trans) {
2943         trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
2944         rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2945
2946         GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT
2947             " for mline %u", trans, i);
2948       } else {
2949         trans = WEBRTC_TRANSCEIVER (rtp_trans);
2950       }
2951
2952       if (!trans->do_nack) {
2953         answer_caps = gst_caps_make_writable (answer_caps);
2954         for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
2955           GstStructure *s = gst_caps_get_structure (answer_caps, k);
2956           gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
2957         }
2958       }
2959
2960       gst_sdp_media_set_media_from_caps (answer_caps, media);
2961
2962       _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
2963           &target_ssrc);
2964
2965       original_target_pt = target_pt;
2966
2967       _media_add_fec (media, trans, offer_caps, &target_pt);
2968       if (trans->do_nack) {
2969         _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
2970         if (target_pt != original_target_pt)
2971           _media_add_rtx (media, trans, offer_caps, original_target_pt,
2972               target_ssrc);
2973       }
2974
2975       if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
2976         _media_add_ssrcs (media, answer_caps, webrtc,
2977             WEBRTC_TRANSCEIVER (rtp_trans));
2978
2979       gst_caps_unref (answer_caps);
2980       answer_caps = NULL;
2981
2982       /* set the new media direction */
2983       offer_dir = _get_direction_from_media (offer_media);
2984       answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
2985       if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
2986         GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
2987             "transceiver direction");
2988         goto rejected;
2989       }
2990       _media_replace_direction (media, answer_dir);
2991
2992       if (!trans->stream) {
2993         TransportStream *item;
2994
2995         item =
2996             _get_or_create_transport_stream (webrtc,
2997             bundled_mids ? bundle_idx : i, FALSE);
2998         webrtc_transceiver_set_transport (trans, item);
2999       }
3000
3001       if (bundled_mids) {
3002         const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
3003
3004         g_assert (mid);
3005         g_string_append_printf (bundled_mids, " %s", mid);
3006       }
3007
3008       /* set the a=fingerprint: for this transport */
3009       _add_fingerprint_to_media (trans->stream->transport, media);
3010
3011       gst_caps_unref (offer_caps);
3012     } else {
3013       GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
3014       goto rejected;
3015     }
3016
3017     if (0) {
3018     rejected:
3019       GST_INFO_OBJECT (webrtc, "media %u rejected", i);
3020       gst_sdp_media_free (media);
3021       gst_sdp_media_copy (offer_media, &media);
3022       gst_sdp_media_set_port_info (media, 0, 0);
3023     }
3024     gst_sdp_message_add_media (ret, media);
3025     gst_sdp_media_free (media);
3026   }
3027
3028   if (bundled_mids) {
3029     gchar *mids = g_string_free (bundled_mids, FALSE);
3030
3031     gst_sdp_message_add_attribute (ret, "group", mids);
3032     g_free (mids);
3033   }
3034
3035   if (bundle_ufrag)
3036     g_free (bundle_ufrag);
3037
3038   if (bundle_pwd)
3039     g_free (bundle_pwd);
3040
3041   /* FIXME: can we add not matched transceivers? */
3042
3043   /* XXX: only true for the initial offerer */
3044   g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
3045
3046 out:
3047   g_strfreev (bundled);
3048
3049   g_list_free (seen_transceivers);
3050
3051   if (webrtc->priv->last_generated_offer)
3052     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
3053   webrtc->priv->last_generated_offer = NULL;
3054   if (webrtc->priv->last_generated_answer)
3055     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
3056   {
3057     GstSDPMessage *copy;
3058     gst_sdp_message_copy (ret, &copy);
3059     webrtc->priv->last_generated_answer =
3060         gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy);
3061   }
3062
3063   return ret;
3064 }
3065
3066 struct create_sdp
3067 {
3068   GstStructure *options;
3069   GstPromise *promise;
3070   GstWebRTCSDPType type;
3071 };
3072
3073 static void
3074 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
3075 {
3076   GstWebRTCSessionDescription *desc = NULL;
3077   GstSDPMessage *sdp = NULL;
3078   GstStructure *s = NULL;
3079
3080   GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
3081       gst_webrtc_sdp_type_to_string (data->type), data->options);
3082
3083   if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
3084     sdp = _create_offer_task (webrtc, data->options);
3085   else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
3086     sdp = _create_answer_task (webrtc, data->options);
3087   else {
3088     g_assert_not_reached ();
3089     goto out;
3090   }
3091
3092   if (sdp) {
3093     desc = gst_webrtc_session_description_new (data->type, sdp);
3094     s = gst_structure_new ("application/x-gst-promise",
3095         gst_webrtc_sdp_type_to_string (data->type),
3096         GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
3097   }
3098
3099 out:
3100   PC_UNLOCK (webrtc);
3101   gst_promise_reply (data->promise, s);
3102   PC_LOCK (webrtc);
3103
3104   if (desc)
3105     gst_webrtc_session_description_free (desc);
3106 }
3107
3108 static void
3109 _free_create_sdp_data (struct create_sdp *data)
3110 {
3111   if (data->options)
3112     gst_structure_free (data->options);
3113   gst_promise_unref (data->promise);
3114   g_free (data);
3115 }
3116
3117 static void
3118 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
3119     const GstStructure * options, GstPromise * promise)
3120 {
3121   struct create_sdp *data = g_new0 (struct create_sdp, 1);
3122
3123   if (options)
3124     data->options = gst_structure_copy (options);
3125   data->promise = gst_promise_ref (promise);
3126   data->type = GST_WEBRTC_SDP_TYPE_OFFER;
3127
3128   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3129       data, (GDestroyNotify) _free_create_sdp_data);
3130 }
3131
3132 static void
3133 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
3134     const GstStructure * options, GstPromise * promise)
3135 {
3136   struct create_sdp *data = g_new0 (struct create_sdp, 1);
3137
3138   if (options)
3139     data->options = gst_structure_copy (options);
3140   data->promise = gst_promise_ref (promise);
3141   data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
3142
3143   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
3144       data, (GDestroyNotify) _free_create_sdp_data);
3145 }
3146
3147 static GstWebRTCBinPad *
3148 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
3149     guint media_idx)
3150 {
3151   GstWebRTCBinPad *pad;
3152   gchar *pad_name;
3153
3154   pad_name =
3155       g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
3156       media_idx);
3157   pad = gst_webrtc_bin_pad_new (pad_name, direction);
3158   g_free (pad_name);
3159   pad->mlineindex = media_idx;
3160
3161   return pad;
3162 }
3163
3164 static GstWebRTCRTPTransceiver *
3165 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
3166     const GstSDPMessage * sdp, guint media_idx)
3167 {
3168   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3169   GstWebRTCRTPTransceiver *ret = NULL;
3170   int i;
3171
3172   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3173     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3174
3175     if (g_strcmp0 (attr->key, "mid") == 0) {
3176       if ((ret =
3177               _find_transceiver (webrtc, attr->value,
3178                   (FindTransceiverFunc) match_for_mid)))
3179         goto out;
3180     }
3181   }
3182
3183   ret = _find_transceiver (webrtc, &media_idx,
3184       (FindTransceiverFunc) transceiver_match_for_mline);
3185
3186 out:
3187   GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
3188   return ret;
3189 }
3190
3191 static GstPad *
3192 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
3193 {
3194 /*
3195  * Not-bundle case:
3196  *
3197  * ,-------------------------webrtcbin-------------------------,
3198  * ;                                                           ;
3199  * ;          ,-------rtpbin-------,   ,--transport_send_%u--, ;
3200  * ;          ;    send_rtp_src_%u o---o rtp_sink            ; ;
3201  * ;          ;                    ;   ;                     ; ;
3202  * ;          ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
3203  * ; sink_%u  ;                    ;   '---------------------' ;
3204  * o----------o send_rtp_sink_%u   ;                           ;
3205  * ;          '--------------------'                           ;
3206  * '--------------------- -------------------------------------'
3207  */
3208
3209 /*
3210  * Bundle case:
3211  * ,--------------------------------webrtcbin--------------------------------,
3212  * ;                                                                         ;
3213  * ;                        ,-------rtpbin-------,   ,--transport_send_%u--, ;
3214  * ;                        ;    send_rtp_src_%u o---o rtp_sink            ; ;
3215  * ;                        ;                    ;   ;                     ; ;
3216  * ;                        ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
3217  * ; sink_%u ,---funnel---, ;                    ;   '---------------------' ;
3218  * o---------o sink_%u    ; ;                    ;                           ;
3219  * ; sink_%u ;        src o-o send_rtp_sink_%u   ;                           ;
3220  * o---------o sink_%u    ; ;                    ;                           ;
3221  * ;         '------------' '--------------------'                           ;
3222  * '-------------------------------------------------------------------------'
3223  */
3224   GstPadTemplate *rtp_templ;
3225   GstPad *rtp_sink;
3226   gchar *pad_name;
3227   WebRTCTransceiver *trans;
3228
3229   g_return_val_if_fail (pad->trans != NULL, NULL);
3230
3231   GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
3232
3233   trans = WEBRTC_TRANSCEIVER (pad->trans);
3234
3235   g_assert (trans->stream);
3236
3237   if (!webrtc->rtpfunnel) {
3238     rtp_templ =
3239         _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
3240         "send_rtp_sink_%u");
3241     g_assert (rtp_templ);
3242
3243     pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
3244     rtp_sink =
3245         gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
3246     g_free (pad_name);
3247     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
3248     gst_object_unref (rtp_sink);
3249
3250     pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
3251     if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3252             GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
3253       g_warn_if_reached ();
3254     g_free (pad_name);
3255   } else {
3256     gchar *pad_name = g_strdup_printf ("sink_%u", pad->mlineindex);
3257     GstPad *funnel_sinkpad =
3258         gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
3259
3260     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
3261
3262     g_free (pad_name);
3263     gst_object_unref (funnel_sinkpad);
3264   }
3265
3266   gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
3267
3268   return GST_PAD (pad);
3269 }
3270
3271 /* output pads are receiving elements */
3272 static void
3273 _connect_output_stream (GstWebRTCBin * webrtc,
3274     TransportStream * stream, guint session_id)
3275 {
3276 /*
3277  * ,------------------------webrtcbin------------------------,
3278  * ;                             ,---------rtpbin---------,  ;
3279  * ; ,-transport_receive_%u--,   ;                        ;  ;
3280  * ; ;               rtp_src o---o recv_rtp_sink_%u       ;  ;
3281  * ; ;                       ;   ;                        ;  ;
3282  * ; ;              rtcp_src o---o recv_rtcp_sink_%u      ;  ;
3283  * ; '-----------------------'   ;                        ;  ; src_%u
3284  * ;                             ;  recv_rtp_src_%u_%u_%u o--o
3285  * ;                             '------------------------'  ;
3286  * '---------------------------------------------------------'
3287  */
3288   gchar *pad_name;
3289
3290   if (stream->output_connected) {
3291     GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already "
3292         "connected to rtpbin.  Not connecting", stream);
3293     return;
3294   }
3295
3296   GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT,
3297       session_id, stream);
3298
3299   pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
3300   if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
3301           "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
3302     g_warn_if_reached ();
3303   g_free (pad_name);
3304
3305   gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
3306
3307   /* The webrtcbin src_%u output pads will be created when rtpbin receives
3308    * data on that stream in on_rtpbin_pad_added() */
3309
3310   stream->output_connected = TRUE;
3311 }
3312
3313 typedef struct
3314 {
3315   guint mlineindex;
3316   gchar *candidate;
3317 } IceCandidateItem;
3318
3319 static void
3320 _clear_ice_candidate_item (IceCandidateItem ** item)
3321 {
3322   g_free ((*item)->candidate);
3323   g_free (*item);
3324 }
3325
3326 static void
3327 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item,
3328     gboolean drop_invalid)
3329 {
3330   GstWebRTCICEStream *stream;
3331
3332   stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
3333   if (stream == NULL) {
3334     if (drop_invalid) {
3335       GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping",
3336           item->mlineindex);
3337     } else {
3338       IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
3339       new->mlineindex = item->mlineindex;
3340       new->candidate = g_strdup (item->candidate);
3341
3342       g_array_append_val (webrtc->priv->pending_ice_candidates, new);
3343       GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex);
3344     }
3345     return;
3346   }
3347
3348   GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
3349       item->mlineindex, item->candidate);
3350
3351   gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
3352 }
3353
3354 static gboolean
3355 _filter_sdp_fields (GQuark field_id, const GValue * value,
3356     GstStructure * new_structure)
3357 {
3358   if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
3359     gst_structure_id_set_value (new_structure, field_id, value);
3360   }
3361   return TRUE;
3362 }
3363
3364 static void
3365 _set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream)
3366 {
3367   gint *rtx_pt;
3368   gsize rtx_count;
3369
3370   rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count);
3371   GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count);
3372   if (rtx_pt) {
3373     GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
3374     gsize i;
3375
3376     for (i = 0; i < rtx_count; i++) {
3377       GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]);
3378       const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
3379       const gchar *apt = gst_structure_get_string (s, "apt");
3380
3381       GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]);
3382       gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL);
3383     }
3384
3385     GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %"
3386         GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive,
3387         stream->rtxsend, pt_map);
3388
3389     if (stream->rtxreceive)
3390       g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL);
3391     if (stream->rtxsend)
3392       g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL);
3393   }
3394 }
3395
3396 static void
3397 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
3398     TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
3399 {
3400   guint i, len;
3401   const gchar *proto;
3402   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3403
3404   /* get proto */
3405   proto = gst_sdp_media_get_proto (media);
3406   if (proto != NULL) {
3407     /* Parse global SDP attributes once */
3408     GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
3409     GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
3410     gst_sdp_message_attributes_to_caps (sdp, global_caps);
3411     GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
3412     gst_sdp_media_attributes_to_caps (media, global_caps);
3413
3414     len = gst_sdp_media_formats_len (media);
3415     for (i = 0; i < len; i++) {
3416       GstCaps *caps, *outcaps;
3417       GstStructure *s;
3418       PtMapItem item;
3419       gint pt;
3420       guint j;
3421
3422       pt = atoi (gst_sdp_media_get_format (media, i));
3423
3424       GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
3425
3426       /* convert caps */
3427       caps = gst_sdp_media_get_caps_from_media (media, pt);
3428       if (caps == NULL) {
3429         GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
3430         continue;
3431       }
3432
3433       /* Merge in global caps */
3434       /* Intersect will merge in missing fields to the current caps */
3435       outcaps = gst_caps_intersect (caps, global_caps);
3436       gst_caps_unref (caps);
3437
3438       s = gst_caps_get_structure (outcaps, 0);
3439       gst_structure_set_name (s, "application/x-rtp");
3440       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
3441         gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
3442
3443       item.caps = gst_caps_new_empty ();
3444
3445       for (j = 0; j < gst_caps_get_size (outcaps); j++) {
3446         GstStructure *s = gst_caps_get_structure (outcaps, j);
3447         GstStructure *filtered =
3448             gst_structure_new_empty (gst_structure_get_name (s));
3449
3450         gst_structure_foreach (s,
3451             (GstStructureForeachFunc) _filter_sdp_fields, filtered);
3452         gst_caps_append_structure (item.caps, filtered);
3453       }
3454
3455       item.pt = pt;
3456       gst_caps_unref (outcaps);
3457
3458       g_array_append_val (stream->ptmap, item);
3459     }
3460
3461     gst_caps_unref (global_caps);
3462   }
3463 }
3464
3465 static void
3466 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
3467     const GstSDPMessage * sdp, guint media_idx,
3468     TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
3469     GStrv bundled, guint bundle_idx)
3470 {
3471   WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
3472   GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
3473   GstWebRTCRTPTransceiverDirection new_dir;
3474   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3475   GstWebRTCDTLSSetup new_setup;
3476   gboolean new_rtcp_mux, new_rtcp_rsize;
3477   ReceiveState receive_state = 0;
3478   int i;
3479
3480   rtp_trans->mline = media_idx;
3481
3482   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3483     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3484
3485     if (g_strcmp0 (attr->key, "mid") == 0) {
3486       g_free (rtp_trans->mid);
3487       rtp_trans->mid = g_strdup (attr->value);
3488     }
3489   }
3490
3491   {
3492     const GstSDPMedia *local_media, *remote_media;
3493     GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
3494     GstWebRTCDTLSSetup local_setup, remote_setup;
3495
3496     local_media =
3497         gst_sdp_message_get_media (webrtc->current_local_description->sdp,
3498         media_idx);
3499     remote_media =
3500         gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
3501         media_idx);
3502
3503     local_setup = _get_dtls_setup_from_media (local_media);
3504     remote_setup = _get_dtls_setup_from_media (remote_media);
3505     new_setup = _get_final_setup (local_setup, remote_setup);
3506     if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
3507       return;
3508
3509     local_dir = _get_direction_from_media (local_media);
3510     remote_dir = _get_direction_from_media (remote_media);
3511     new_dir = _get_final_direction (local_dir, remote_dir);
3512
3513     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3514       return;
3515
3516     if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
3517         && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE
3518         && prev_dir != new_dir) {
3519       GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
3520       return;
3521     }
3522
3523     if (!bundled || bundle_idx == media_idx) {
3524       new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
3525           && _media_has_attribute_key (remote_media, "rtcp-mux");
3526       new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
3527           && _media_has_attribute_key (remote_media, "rtcp-rsize");
3528
3529       {
3530         GObject *session;
3531         g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
3532             media_idx, &session);
3533         if (session) {
3534           g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
3535           g_object_unref (session);
3536         }
3537       }
3538
3539       g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
3540     }
3541   }
3542
3543   if (new_dir != prev_dir) {
3544     GST_TRACE_OBJECT (webrtc, "transceiver direction change");
3545
3546     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
3547       GstWebRTCBinPad *pad;
3548
3549       pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
3550       if (pad) {
3551         GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
3552         if (target) {
3553           GstPad *peer = gst_pad_get_peer (target);
3554           if (peer) {
3555             gst_pad_send_event (peer, gst_event_new_eos ());
3556             gst_object_unref (peer);
3557           }
3558           gst_object_unref (target);
3559         }
3560         gst_object_unref (pad);
3561       }
3562
3563       /* XXX: send eos event up the sink pad as well? */
3564     }
3565
3566     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
3567         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
3568       GstWebRTCBinPad *pad =
3569           _find_pad_for_mline (webrtc, GST_PAD_SINK, media_idx);
3570       if (pad) {
3571         GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
3572             " for transceiver %" GST_PTR_FORMAT, pad, trans);
3573         g_assert (pad->trans == rtp_trans);
3574         g_assert (pad->mlineindex == media_idx);
3575         gst_object_unref (pad);
3576       } else {
3577         GST_DEBUG_OBJECT (webrtc,
3578             "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
3579         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, media_idx);
3580         pad->trans = gst_object_ref (rtp_trans);
3581         _connect_input_stream (webrtc, pad);
3582         _add_pad (webrtc, pad);
3583       }
3584     }
3585     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
3586         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
3587       GstWebRTCBinPad *pad =
3588           _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
3589       if (pad) {
3590         GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
3591             " for transceiver %" GST_PTR_FORMAT, pad, trans);
3592         g_assert (pad->trans == rtp_trans);
3593         g_assert (pad->mlineindex == media_idx);
3594         gst_object_unref (pad);
3595       } else {
3596         GST_DEBUG_OBJECT (webrtc,
3597             "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
3598         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
3599         pad->trans = gst_object_ref (rtp_trans);
3600
3601         if (!trans->stream) {
3602           TransportStream *item;
3603
3604           item =
3605               _get_or_create_transport_stream (webrtc,
3606               bundled ? bundle_idx : media_idx, FALSE);
3607           webrtc_transceiver_set_transport (trans, item);
3608         }
3609
3610         _connect_output_stream (webrtc, trans->stream,
3611             bundled ? bundle_idx : media_idx);
3612         /* delay adding the pad until rtpbin creates the recv output pad
3613          * to ghost to so queries/events travel through the pipeline correctly
3614          * as soon as the pad is added */
3615         _add_pad_to_list (webrtc, pad);
3616       }
3617
3618       receive_state = RECEIVE_STATE_PASS;
3619     } else if (!bundled) {
3620       receive_state = RECEIVE_STATE_DROP;
3621     }
3622
3623     rtp_trans->mline = media_idx;
3624     rtp_trans->current_direction = new_dir;
3625   }
3626
3627   if (!bundled || bundle_idx == media_idx) {
3628     if (stream->rtxsend || stream->rtxreceive) {
3629       _set_rtx_ptmap_from_stream (webrtc, stream);
3630     }
3631
3632     g_object_set (stream, "dtls-client",
3633         new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
3634   }
3635
3636   /* Must be after setting the "dtls-client" so that data is not pushed into
3637    * the dtlssrtp elements before the ssl direction has been set which will
3638    * throw SSL errors */
3639   if (receive_state > 0)
3640     transport_receive_bin_set_receive_state (stream->receive_bin,
3641         receive_state);
3642 }
3643
3644 /* must be called with the pc lock held */
3645 static gint
3646 _generate_data_channel_id (GstWebRTCBin * webrtc)
3647 {
3648   gboolean is_client;
3649   gint new_id = -1, max_channels = 0;
3650
3651   if (webrtc->priv->sctp_transport) {
3652     g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
3653         NULL);
3654   }
3655   if (max_channels <= 0) {
3656     max_channels = 65534;
3657   }
3658
3659   g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
3660       NULL);
3661
3662   /* TODO: a better search algorithm */
3663   do {
3664     GstWebRTCDataChannel *channel;
3665
3666     new_id++;
3667
3668     if (new_id < 0 || new_id >= max_channels) {
3669       /* exhausted id space */
3670       GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
3671           "data channel id (max %i)", max_channels);
3672       return -1;
3673     }
3674
3675     /* client must generate even ids, server must generate odd ids */
3676     if (new_id % 2 == ! !is_client)
3677       continue;
3678
3679     channel = _find_data_channel_for_id (webrtc, new_id);
3680     if (!channel)
3681       break;
3682   } while (TRUE);
3683
3684   return new_id;
3685 }
3686
3687 static void
3688 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
3689     const GstSDPMessage * sdp, guint media_idx, TransportStream * stream)
3690 {
3691   const GstSDPMedia *local_media, *remote_media;
3692   GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
3693   TransportReceiveBin *receive;
3694   int local_port, remote_port;
3695   guint64 local_max_size, remote_max_size, max_size;
3696   int i;
3697
3698   local_media =
3699       gst_sdp_message_get_media (webrtc->current_local_description->sdp,
3700       media_idx);
3701   remote_media =
3702       gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
3703       media_idx);
3704
3705   local_setup = _get_dtls_setup_from_media (local_media);
3706   remote_setup = _get_dtls_setup_from_media (remote_media);
3707   new_setup = _get_final_setup (local_setup, remote_setup);
3708   if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
3709     return;
3710
3711   /* data channel is always rtcp-muxed to avoid generating ICE candidates
3712    * for RTCP */
3713   g_object_set (stream, "rtcp-mux", TRUE, "dtls-client",
3714       new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
3715
3716   local_port = _get_sctp_port_from_media (local_media);
3717   remote_port = _get_sctp_port_from_media (local_media);
3718   if (local_port == -1 || remote_port == -1)
3719     return;
3720
3721   if (0 == (local_max_size =
3722           _get_sctp_max_message_size_from_media (local_media)))
3723     local_max_size = G_MAXUINT64;
3724   if (0 == (remote_max_size =
3725           _get_sctp_max_message_size_from_media (remote_media)))
3726     remote_max_size = G_MAXUINT64;
3727   max_size = MIN (local_max_size, remote_max_size);
3728
3729   webrtc->priv->sctp_transport->max_message_size = max_size;
3730
3731   {
3732     guint orig_local_port, orig_remote_port;
3733
3734     /* XXX: sctpassociation warns if we are in the wrong state */
3735     g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
3736         &orig_local_port, NULL);
3737
3738     if (orig_local_port != local_port)
3739       g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
3740           local_port, NULL);
3741
3742     g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
3743         &orig_remote_port, NULL);
3744     if (orig_remote_port != remote_port)
3745       g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
3746           remote_port, NULL);
3747   }
3748
3749   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
3750     GstWebRTCDataChannel *channel;
3751
3752     channel =
3753         g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, i);
3754
3755     if (channel->id == -1)
3756       channel->id = _generate_data_channel_id (webrtc);
3757     if (channel->id == -1)
3758       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
3759           ("%s", "Failed to generate an identifier for a data channel"), NULL);
3760
3761     if (webrtc->priv->sctp_transport->association_established
3762         && !channel->negotiated && !channel->opened) {
3763       gst_webrtc_data_channel_link_to_sctp (channel,
3764           webrtc->priv->sctp_transport);
3765       gst_webrtc_data_channel_start_negotiation (channel);
3766     }
3767   }
3768
3769   receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
3770   transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
3771 }
3772
3773 static gboolean
3774 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
3775     gconstpointer data)
3776 {
3777   if (p1->mid)
3778     return FALSE;
3779   if (p1->mline != -1)
3780     return FALSE;
3781   if (p1->stopped)
3782     return FALSE;
3783
3784   return TRUE;
3785 }
3786
3787 static void
3788 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
3789 {
3790   gchar *pad_name;
3791   GstPad *queue_srcpad;
3792   GstPad *rtp_sink;
3793   TransportStream *stream = _find_transport_for_session (webrtc, session_id);
3794   GstElement *queue;
3795
3796   g_assert (stream);
3797
3798   if (webrtc->rtpfunnel)
3799     goto done;
3800
3801   webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
3802   gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
3803   gst_element_sync_state_with_parent (webrtc->rtpfunnel);
3804
3805   queue = gst_element_factory_make ("queue", NULL);
3806   gst_bin_add (GST_BIN (webrtc), queue);
3807   gst_element_sync_state_with_parent (queue);
3808
3809   gst_element_link (webrtc->rtpfunnel, queue);
3810
3811   queue_srcpad = gst_element_get_static_pad (queue, "src");
3812
3813   pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
3814   rtp_sink = gst_element_get_request_pad (webrtc->rtpbin, pad_name);
3815   g_free (pad_name);
3816   gst_pad_link (queue_srcpad, rtp_sink);
3817   gst_object_unref (queue_srcpad);
3818   gst_object_unref (rtp_sink);
3819
3820   pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
3821   if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3822           GST_ELEMENT (stream->send_bin), "rtp_sink"))
3823     g_warn_if_reached ();
3824   g_free (pad_name);
3825
3826 done:
3827   return;
3828 }
3829
3830 static gboolean
3831 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
3832     GstWebRTCSessionDescription * sdp)
3833 {
3834   int i;
3835   gboolean ret = FALSE;
3836   GStrv bundled = NULL;
3837   guint bundle_idx = 0;
3838   TransportStream *bundle_stream = NULL;
3839
3840   if (!_parse_bundle (sdp->sdp, &bundled))
3841     goto done;
3842
3843   if (bundled) {
3844
3845     if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
3846       GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
3847           bundled[0]);
3848       goto done;
3849     }
3850
3851     bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
3852         _message_media_is_datachannel (sdp->sdp, bundle_idx));
3853
3854     _connect_rtpfunnel (webrtc, bundle_idx);
3855
3856     g_array_set_size (bundle_stream->ptmap, 0);
3857     for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
3858       _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
3859     }
3860   }
3861
3862   for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
3863     const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
3864     TransportStream *stream;
3865     GstWebRTCRTPTransceiver *trans;
3866     guint transport_idx;
3867
3868     /* skip rejected media */
3869     if (gst_sdp_media_get_port (media) == 0)
3870       continue;
3871
3872     if (bundled)
3873       transport_idx = bundle_idx;
3874     else
3875       transport_idx = i;
3876
3877     trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
3878
3879     stream = _get_or_create_transport_stream (webrtc, transport_idx,
3880         _message_media_is_datachannel (sdp->sdp, transport_idx));
3881     if (!bundled) {
3882       g_array_set_size (stream->ptmap, 0);
3883       _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
3884     }
3885
3886     if (trans)
3887       webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
3888
3889     if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
3890       GST_ERROR ("State mismatch.  Could not find local transceiver by mline.");
3891       goto done;
3892     } else {
3893       if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
3894           g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
3895         if (trans) {
3896           _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
3897               trans, bundled, bundle_idx);
3898         } else {
3899           trans = _find_transceiver (webrtc, NULL,
3900               (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
3901           /* XXX: default to the advertised direction in the sdp for new
3902            * transceviers.  The spec doesn't actually say what happens here, only
3903            * that calls to setDirection will change the value.  Nothing about
3904            * a default value when the transceiver is created internally */
3905           if (!trans) {
3906             trans =
3907                 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
3908                     _get_direction_from_media (media), i));
3909           }
3910           _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
3911               trans, bundled, bundle_idx);
3912         }
3913       } else if (_message_media_is_datachannel (sdp->sdp, i)) {
3914         _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream);
3915       } else {
3916         GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
3917       }
3918     }
3919   }
3920
3921   ret = TRUE;
3922
3923 done:
3924   g_strfreev (bundled);
3925
3926   return ret;
3927 }
3928
3929 struct set_description
3930 {
3931   GstPromise *promise;
3932   SDPSource source;
3933   GstWebRTCSessionDescription *sdp;
3934 };
3935
3936 /* http://w3c.github.io/webrtc-pc/#set-description */
3937 static void
3938 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
3939 {
3940   GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
3941   gboolean signalling_state_changed = FALSE;
3942   GError *error = NULL;
3943   GStrv bundled = NULL;
3944   guint bundle_idx = 0;
3945   guint i;
3946
3947   {
3948     gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
3949         webrtc->signaling_state);
3950     gchar *type_str =
3951         _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
3952     gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
3953     GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
3954         _sdp_source_to_string (sd->source), type_str, state);
3955     GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
3956     g_free (sdp_text);
3957     g_free (state);
3958     g_free (type_str);
3959   }
3960
3961   if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error)) {
3962     GST_ERROR_OBJECT (webrtc, "%s", error->message);
3963     g_clear_error (&error);
3964     goto out;
3965   }
3966
3967   if (webrtc->priv->is_closed) {
3968     GST_WARNING_OBJECT (webrtc, "we are closed");
3969     goto out;
3970   }
3971
3972   if (!_parse_bundle (sd->sdp->sdp, &bundled))
3973     goto out;
3974
3975   if (bundled) {
3976     if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
3977       GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
3978           bundled[0]);
3979       goto out;
3980     }
3981   }
3982
3983   switch (sd->sdp->type) {
3984     case GST_WEBRTC_SDP_TYPE_OFFER:{
3985       if (sd->source == SDP_LOCAL) {
3986         if (webrtc->pending_local_description)
3987           gst_webrtc_session_description_free
3988               (webrtc->pending_local_description);
3989         webrtc->pending_local_description =
3990             gst_webrtc_session_description_copy (sd->sdp);
3991         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
3992       } else {
3993         if (webrtc->pending_remote_description)
3994           gst_webrtc_session_description_free
3995               (webrtc->pending_remote_description);
3996         webrtc->pending_remote_description =
3997             gst_webrtc_session_description_copy (sd->sdp);
3998         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
3999       }
4000       break;
4001     }
4002     case GST_WEBRTC_SDP_TYPE_ANSWER:{
4003       if (sd->source == SDP_LOCAL) {
4004         if (webrtc->current_local_description)
4005           gst_webrtc_session_description_free
4006               (webrtc->current_local_description);
4007         webrtc->current_local_description =
4008             gst_webrtc_session_description_copy (sd->sdp);
4009
4010         if (webrtc->current_remote_description)
4011           gst_webrtc_session_description_free
4012               (webrtc->current_remote_description);
4013         webrtc->current_remote_description = webrtc->pending_remote_description;
4014         webrtc->pending_remote_description = NULL;
4015       } else {
4016         if (webrtc->current_remote_description)
4017           gst_webrtc_session_description_free
4018               (webrtc->current_remote_description);
4019         webrtc->current_remote_description =
4020             gst_webrtc_session_description_copy (sd->sdp);
4021
4022         if (webrtc->current_local_description)
4023           gst_webrtc_session_description_free
4024               (webrtc->current_local_description);
4025         webrtc->current_local_description = webrtc->pending_local_description;
4026         webrtc->pending_local_description = NULL;
4027       }
4028
4029       if (webrtc->pending_local_description)
4030         gst_webrtc_session_description_free (webrtc->pending_local_description);
4031       webrtc->pending_local_description = NULL;
4032
4033       if (webrtc->pending_remote_description)
4034         gst_webrtc_session_description_free
4035             (webrtc->pending_remote_description);
4036       webrtc->pending_remote_description = NULL;
4037
4038       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4039       break;
4040     }
4041     case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
4042       GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
4043       if (sd->source == SDP_LOCAL) {
4044         if (webrtc->pending_local_description)
4045           gst_webrtc_session_description_free
4046               (webrtc->pending_local_description);
4047         webrtc->pending_local_description = NULL;
4048       } else {
4049         if (webrtc->pending_remote_description)
4050           gst_webrtc_session_description_free
4051               (webrtc->pending_remote_description);
4052         webrtc->pending_remote_description = NULL;
4053       }
4054
4055       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
4056       break;
4057     }
4058     case GST_WEBRTC_SDP_TYPE_PRANSWER:{
4059       GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
4060       if (sd->source == SDP_LOCAL) {
4061         if (webrtc->pending_local_description)
4062           gst_webrtc_session_description_free
4063               (webrtc->pending_local_description);
4064         webrtc->pending_local_description =
4065             gst_webrtc_session_description_copy (sd->sdp);
4066
4067         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
4068       } else {
4069         if (webrtc->pending_remote_description)
4070           gst_webrtc_session_description_free
4071               (webrtc->pending_remote_description);
4072         webrtc->pending_remote_description =
4073             gst_webrtc_session_description_copy (sd->sdp);
4074
4075         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
4076       }
4077       break;
4078     }
4079   }
4080
4081   if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
4082     /* FIXME:
4083      * If the mid value of an RTCRtpTransceiver was set to a non-null value
4084      * by the RTCSessionDescription that is being rolled back, set the mid
4085      * value of that transceiver to null, as described by [JSEP]
4086      * (section 4.1.7.2.).
4087      * If an RTCRtpTransceiver was created by applying the
4088      * RTCSessionDescription that is being rolled back, and a track has not
4089      * been attached to it via addTrack, remove that transceiver from
4090      * connection's set of transceivers, as described by [JSEP]
4091      * (section 4.1.7.2.).
4092      * Restore the value of connection's [[ sctpTransport]] internal slot
4093      * to its value at the last stable signaling state.
4094      */
4095   }
4096
4097   if (webrtc->signaling_state != new_signaling_state) {
4098     webrtc->signaling_state = new_signaling_state;
4099     signalling_state_changed = TRUE;
4100   }
4101
4102   if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
4103     GList *tmp;
4104
4105     /* media modifications */
4106     _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp);
4107
4108     for (tmp = webrtc->priv->pending_sink_transceivers; tmp; tmp = tmp->next) {
4109       GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
4110       const GstSDPMedia *media;
4111
4112       media = gst_sdp_message_get_media (sd->sdp->sdp, pad->mlineindex);
4113       /* skip rejected media */
4114       /* FIXME: arrange for an appropriate flow return */
4115       if (gst_sdp_media_get_port (media) == 0)
4116         continue;
4117
4118       _connect_input_stream (webrtc, pad);
4119       gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
4120       pad->block_id = 0;
4121     }
4122
4123     g_list_free_full (webrtc->priv->pending_sink_transceivers,
4124         (GDestroyNotify) gst_object_unref);
4125     webrtc->priv->pending_sink_transceivers = NULL;
4126   }
4127
4128   for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
4129     gchar *ufrag, *pwd;
4130     TransportStream *item;
4131
4132     item =
4133         _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
4134         _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
4135
4136     if (sd->source == SDP_REMOTE) {
4137       const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
4138       guint j;
4139
4140       for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
4141         const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
4142
4143         if (g_strcmp0 (attr->key, "ssrc") == 0) {
4144           GStrv split = g_strsplit (attr->value, " ", 0);
4145           guint32 ssrc;
4146
4147           if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
4148               && g_str_has_prefix (split[1], "cname:")) {
4149             SsrcMapItem ssrc_item;
4150
4151             ssrc_item.media_idx = i;
4152             ssrc_item.ssrc = ssrc;
4153             g_array_append_val (item->remote_ssrcmap, ssrc_item);
4154           }
4155           g_strfreev (split);
4156         }
4157       }
4158     }
4159
4160     if (bundled && bundle_idx != i)
4161       continue;
4162
4163     _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
4164
4165     if (sd->source == SDP_LOCAL)
4166       gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
4167           item->stream, ufrag, pwd);
4168     else
4169       gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
4170           item->stream, ufrag, pwd);
4171     g_free (ufrag);
4172     g_free (pwd);
4173   }
4174
4175   for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
4176     IceStreamItem *item =
4177         &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
4178
4179     gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
4180   }
4181
4182   if (webrtc->current_local_description && webrtc->current_remote_description) {
4183     int i;
4184
4185     for (i = 0; i < webrtc->priv->pending_ice_candidates->len; i++) {
4186       IceCandidateItem *item =
4187           g_array_index (webrtc->priv->pending_ice_candidates,
4188           IceCandidateItem *, i);
4189
4190       _add_ice_candidate (webrtc, item, TRUE);
4191     }
4192     g_array_set_size (webrtc->priv->pending_ice_candidates, 0);
4193   }
4194
4195   /*
4196    * If connection's signaling state changed above, fire an event named
4197    * signalingstatechange at connection.
4198    */
4199   if (signalling_state_changed) {
4200     gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4201         webrtc->signaling_state);
4202     gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
4203         new_signaling_state);
4204     GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
4205         "to %s", from, to);
4206     PC_UNLOCK (webrtc);
4207     g_object_notify (G_OBJECT (webrtc), "signaling-state");
4208     PC_LOCK (webrtc);
4209
4210     g_free (from);
4211     g_free (to);
4212   }
4213
4214   if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
4215     gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
4216
4217     /* If connection's signaling state is now stable, update the
4218      * negotiation-needed flag. If connection's [[ needNegotiation]] slot
4219      * was true both before and after this update, queue a task to check
4220      * connection's [[needNegotiation]] slot and, if still true, fire a
4221      * simple event named negotiationneeded at connection.*/
4222     _update_need_negotiation (webrtc);
4223     if (prev_need_negotiation && webrtc->priv->need_negotiation) {
4224       _check_need_negotiation_task (webrtc, NULL);
4225     }
4226   }
4227
4228 out:
4229   g_strfreev (bundled);
4230
4231   PC_UNLOCK (webrtc);
4232   gst_promise_reply (sd->promise, NULL);
4233   PC_LOCK (webrtc);
4234 }
4235
4236 static void
4237 _free_set_description_data (struct set_description *sd)
4238 {
4239   if (sd->promise)
4240     gst_promise_unref (sd->promise);
4241   if (sd->sdp)
4242     gst_webrtc_session_description_free (sd->sdp);
4243   g_free (sd);
4244 }
4245
4246 static void
4247 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
4248     GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
4249 {
4250   struct set_description *sd;
4251
4252   if (remote_sdp == NULL)
4253     goto bad_input;
4254   if (remote_sdp->sdp == NULL)
4255     goto bad_input;
4256
4257   sd = g_new0 (struct set_description, 1);
4258   if (promise != NULL)
4259     sd->promise = gst_promise_ref (promise);
4260   sd->source = SDP_REMOTE;
4261   sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
4262
4263   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
4264       sd, (GDestroyNotify) _free_set_description_data);
4265
4266   return;
4267
4268 bad_input:
4269   {
4270     gst_promise_reply (promise, NULL);
4271     g_return_if_reached ();
4272   }
4273 }
4274
4275 static void
4276 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
4277     GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
4278 {
4279   struct set_description *sd;
4280
4281   if (local_sdp == NULL)
4282     goto bad_input;
4283   if (local_sdp->sdp == NULL)
4284     goto bad_input;
4285
4286   sd = g_new0 (struct set_description, 1);
4287   if (promise != NULL)
4288     sd->promise = gst_promise_ref (promise);
4289   sd->source = SDP_LOCAL;
4290   sd->sdp = gst_webrtc_session_description_copy (local_sdp);
4291
4292   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
4293       sd, (GDestroyNotify) _free_set_description_data);
4294
4295   return;
4296
4297 bad_input:
4298   {
4299     gst_promise_reply (promise, NULL);
4300     g_return_if_reached ();
4301   }
4302 }
4303
4304 static void
4305 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
4306 {
4307   if (!webrtc->current_local_description || !webrtc->current_remote_description) {
4308     IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
4309     new->mlineindex = item->mlineindex;
4310     new->candidate = g_strdup (item->candidate);
4311
4312     g_array_append_val (webrtc->priv->pending_ice_candidates, new);
4313   } else {
4314     _add_ice_candidate (webrtc, item, FALSE);
4315   }
4316 }
4317
4318 static void
4319 _free_ice_candidate_item (IceCandidateItem * item)
4320 {
4321   _clear_ice_candidate_item (&item);
4322 }
4323
4324 static void
4325 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
4326     const gchar * attr)
4327 {
4328   IceCandidateItem *item;
4329
4330   item = g_new0 (IceCandidateItem, 1);
4331   item->mlineindex = mline;
4332   if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
4333     item->candidate = g_strdup (attr);
4334   else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
4335     item->candidate = g_strdup_printf ("a=%s", attr);
4336   gst_webrtc_bin_enqueue_task (webrtc,
4337       (GstWebRTCBinFunc) _add_ice_candidate_task, item,
4338       (GDestroyNotify) _free_ice_candidate_item);
4339 }
4340
4341 static void
4342 _on_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
4343 {
4344   const gchar *cand = item->candidate;
4345
4346   if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
4347     /* stripping away "a=" */
4348     cand += 2;
4349   }
4350
4351   GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
4352       item->mlineindex, cand);
4353
4354   PC_UNLOCK (webrtc);
4355   g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
4356       0, item->mlineindex, cand);
4357   PC_LOCK (webrtc);
4358 }
4359
4360 static void
4361 _on_ice_candidate (GstWebRTCICE * ice, guint session_id,
4362     gchar * candidate, GstWebRTCBin * webrtc)
4363 {
4364   IceCandidateItem *item = g_new0 (IceCandidateItem, 1);
4365
4366   item->mlineindex = session_id;
4367   item->candidate = g_strdup (candidate);
4368
4369   gst_webrtc_bin_enqueue_task (webrtc,
4370       (GstWebRTCBinFunc) _on_ice_candidate_task, item,
4371       (GDestroyNotify) _free_ice_candidate_item);
4372 }
4373
4374 /* https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm */
4375 static GstStructure *
4376 _get_stats_from_selector (GstWebRTCBin * webrtc, gpointer selector)
4377 {
4378   if (selector)
4379     GST_FIXME_OBJECT (webrtc, "Implement stats selection");
4380
4381   return gst_structure_copy (webrtc->priv->stats);
4382 }
4383
4384 struct get_stats
4385 {
4386   GstPad *pad;
4387   GstPromise *promise;
4388 };
4389
4390 static void
4391 _free_get_stats (struct get_stats *stats)
4392 {
4393   if (stats->pad)
4394     gst_object_unref (stats->pad);
4395   if (stats->promise)
4396     gst_promise_unref (stats->promise);
4397   g_free (stats);
4398 }
4399
4400 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
4401 static void
4402 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
4403 {
4404   GstStructure *s;
4405   gpointer selector = NULL;
4406
4407   gst_webrtc_bin_update_stats (webrtc);
4408
4409   if (stats->pad) {
4410     GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (stats->pad);
4411
4412     if (wpad->trans) {
4413       if (GST_PAD_DIRECTION (wpad) == GST_PAD_SRC) {
4414         selector = wpad->trans->receiver;
4415       } else {
4416         selector = wpad->trans->sender;
4417       }
4418     }
4419   }
4420
4421   s = _get_stats_from_selector (webrtc, selector);
4422   gst_promise_reply (stats->promise, s);
4423 }
4424
4425 static void
4426 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
4427     GstPromise * promise)
4428 {
4429   struct get_stats *stats;
4430
4431   g_return_if_fail (promise != NULL);
4432   g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
4433
4434   stats = g_new0 (struct get_stats, 1);
4435   stats->promise = gst_promise_ref (promise);
4436   /* FIXME: check that pad exists in element */
4437   if (pad)
4438     stats->pad = gst_object_ref (pad);
4439
4440   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
4441       stats, (GDestroyNotify) _free_get_stats);
4442 }
4443
4444 static GstWebRTCRTPTransceiver *
4445 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
4446     GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
4447 {
4448   WebRTCTransceiver *trans;
4449   GstWebRTCRTPTransceiver *rtp_trans;
4450
4451   g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
4452       NULL);
4453
4454   trans = _create_webrtc_transceiver (webrtc, direction, -1);
4455   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4456   if (caps)
4457     rtp_trans->codec_preferences = gst_caps_ref (caps);
4458
4459   return gst_object_ref (trans);
4460 }
4461
4462 static void
4463 _deref_and_unref (GstObject ** object)
4464 {
4465   if (object)
4466     gst_object_unref (*object);
4467 }
4468
4469 static GArray *
4470 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
4471 {
4472   GArray *arr = g_array_new (FALSE, TRUE, sizeof (gpointer));
4473   int i;
4474
4475   g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
4476
4477   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4478     GstWebRTCRTPTransceiver *trans =
4479         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
4480         i);
4481     gst_object_ref (trans);
4482     g_array_append_val (arr, trans);
4483   }
4484
4485   return arr;
4486 }
4487
4488 static GstWebRTCRTPTransceiver *
4489 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
4490 {
4491   GstWebRTCRTPTransceiver *trans = NULL;
4492
4493   if (idx >= webrtc->priv->transceivers->len) {
4494     GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
4495     goto done;
4496   }
4497
4498   trans =
4499       g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
4500       idx);
4501   gst_object_ref (trans);
4502
4503 done:
4504   return trans;
4505 }
4506
4507 static gboolean
4508 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
4509 {
4510   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
4511   g_return_val_if_fail (uri != NULL, FALSE);
4512
4513   GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
4514
4515   return gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
4516 }
4517
4518 static gboolean
4519 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
4520 {
4521   GstPad *gpad = GST_PAD_CAST (user_data);
4522
4523   GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
4524   gst_pad_store_sticky_event (gpad, *event);
4525
4526   return TRUE;
4527 }
4528
4529 static GstWebRTCDataChannel *
4530 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
4531     GstStructure * init_params)
4532 {
4533   gboolean ordered;
4534   gint max_packet_lifetime;
4535   gint max_retransmits;
4536   const gchar *protocol;
4537   gboolean negotiated;
4538   gint id;
4539   GstWebRTCPriorityType priority;
4540   GstWebRTCDataChannel *ret;
4541   gint max_channels = 65534;
4542
4543   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
4544   g_return_val_if_fail (label != NULL, NULL);
4545   g_return_val_if_fail (strlen (label) <= 65535, NULL);
4546   g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
4547
4548   if (!init_params
4549       || !gst_structure_get_boolean (init_params, "ordered", &ordered))
4550     ordered = TRUE;
4551   if (!init_params
4552       || !gst_structure_get_int (init_params, "max-packet-lifetime",
4553           &max_packet_lifetime))
4554     max_packet_lifetime = -1;
4555   if (!init_params
4556       || !gst_structure_get_int (init_params, "max-retransmits",
4557           &max_retransmits))
4558     max_retransmits = -1;
4559   /* both retransmits and lifetime cannot be set */
4560   g_return_val_if_fail ((max_packet_lifetime == -1)
4561       || (max_retransmits == -1), NULL);
4562
4563   if (!init_params
4564       || !(protocol = gst_structure_get_string (init_params, "protocol")))
4565     protocol = "";
4566   g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
4567
4568   if (!init_params
4569       || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
4570     negotiated = FALSE;
4571   if (!negotiated || !init_params
4572       || !gst_structure_get_int (init_params, "id", &id))
4573     id = -1;
4574   if (negotiated)
4575     g_return_val_if_fail (id != -1, NULL);
4576   g_return_val_if_fail (id < 65535, NULL);
4577
4578   if (!init_params
4579       || !gst_structure_get_enum (init_params, "priority",
4580           GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
4581     priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
4582
4583   /* FIXME: clamp max-retransmits and max-packet-lifetime */
4584
4585   if (webrtc->priv->sctp_transport) {
4586     /* Let transport be the connection's [[SctpTransport]] slot.
4587      *
4588      * If the [[DataChannelId]] slot is not null, transport is in 
4589      * connected state and [[DataChannelId]] is greater or equal to the
4590      * transport's [[MaxChannels]] slot, throw an OperationError.
4591      */
4592     g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
4593         NULL);
4594
4595     g_return_val_if_fail (id <= max_channels, NULL);
4596   }
4597
4598   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
4599       !_have_sctp_elements (webrtc))
4600     return NULL;
4601
4602   PC_LOCK (webrtc);
4603   /* check if the id has been used already */
4604   if (id != -1) {
4605     GstWebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
4606     if (channel) {
4607       GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
4608           ("Attempting to add a data channel with a duplicate ID: %i", id),
4609           NULL);
4610       PC_UNLOCK (webrtc);
4611       return NULL;
4612     }
4613   } else if (webrtc->current_local_description
4614       && webrtc->current_remote_description && webrtc->priv->sctp_transport
4615       && webrtc->priv->sctp_transport->transport) {
4616     /* else we can only generate an id if we're configured already.  The other
4617      * case for generating an id is on sdp setting */
4618     id = _generate_data_channel_id (webrtc);
4619     if (id == -1) {
4620       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
4621           ("%s", "Failed to generate an identifier for a data channel"), NULL);
4622       PC_UNLOCK (webrtc);
4623       return NULL;
4624     }
4625   }
4626
4627   ret = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, "label", label,
4628       "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
4629       "max-retransmits", max_retransmits, "protocol", protocol,
4630       "negotiated", negotiated, "id", id, "priority", priority, NULL);
4631
4632   if (ret) {
4633     gst_bin_add (GST_BIN (webrtc), ret->appsrc);
4634     gst_bin_add (GST_BIN (webrtc), ret->appsink);
4635
4636     gst_element_sync_state_with_parent (ret->appsrc);
4637     gst_element_sync_state_with_parent (ret->appsink);
4638
4639     ret = gst_object_ref (ret);
4640     ret->webrtcbin = webrtc;
4641     g_array_append_val (webrtc->priv->data_channels, ret);
4642     gst_webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
4643     if (webrtc->priv->sctp_transport &&
4644         webrtc->priv->sctp_transport->association_established
4645         && !ret->negotiated) {
4646       gst_webrtc_data_channel_start_negotiation (ret);
4647     } else {
4648       _update_need_negotiation (webrtc);
4649     }
4650   }
4651
4652   PC_UNLOCK (webrtc);
4653   return ret;
4654 }
4655
4656 /* === rtpbin signal implementations === */
4657
4658 static void
4659 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
4660     GstWebRTCBin * webrtc)
4661 {
4662   gchar *new_pad_name = NULL;
4663
4664   new_pad_name = gst_pad_get_name (new_pad);
4665   GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
4666   if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
4667     guint32 session_id = 0, ssrc = 0, pt = 0;
4668     GstWebRTCRTPTransceiver *rtp_trans;
4669     WebRTCTransceiver *trans;
4670     TransportStream *stream;
4671     GstWebRTCBinPad *pad;
4672     guint media_idx = 0;
4673     gboolean found_ssrc = FALSE;
4674     guint i;
4675
4676     if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
4677             &pt) != 3) {
4678       g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
4679       return;
4680     }
4681
4682     stream = _find_transport_for_session (webrtc, session_id);
4683     if (!stream)
4684       g_warn_if_reached ();
4685
4686     media_idx = session_id;
4687
4688     for (i = 0; i < stream->remote_ssrcmap->len; i++) {
4689       SsrcMapItem *item =
4690           &g_array_index (stream->remote_ssrcmap, SsrcMapItem, i);
4691       if (item->ssrc == ssrc) {
4692         media_idx = item->media_idx;
4693         found_ssrc = TRUE;
4694         break;
4695       }
4696     }
4697
4698     if (!found_ssrc) {
4699       GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
4700     }
4701
4702     rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
4703     if (!rtp_trans)
4704       g_warn_if_reached ();
4705     trans = WEBRTC_TRANSCEIVER (rtp_trans);
4706     g_assert (trans->stream == stream);
4707
4708     pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4709
4710     GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
4711         " for rtpbin pad name %s", pad, new_pad_name);
4712     if (!pad)
4713       g_warn_if_reached ();
4714     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
4715
4716     if (webrtc->priv->running)
4717       gst_pad_set_active (GST_PAD (pad), TRUE);
4718     gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
4719     gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
4720     _remove_pending_pad (webrtc, pad);
4721
4722     gst_object_unref (pad);
4723   }
4724   g_free (new_pad_name);
4725 }
4726
4727 /* only used for the receiving streams */
4728 static GstCaps *
4729 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
4730     GstWebRTCBin * webrtc)
4731 {
4732   TransportStream *stream;
4733   GstCaps *ret;
4734
4735   GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
4736       session_id);
4737
4738   stream = _find_transport_for_session (webrtc, session_id);
4739   if (!stream)
4740     goto unknown_session;
4741
4742   if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
4743     gst_caps_ref (ret);
4744
4745   GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
4746       "session %d", ret, pt, session_id);
4747
4748   return ret;
4749
4750 unknown_session:
4751   {
4752     GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
4753     return NULL;
4754   }
4755 }
4756
4757 static GstElement *
4758 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
4759     GstWebRTCBin * webrtc)
4760 {
4761   TransportStream *stream;
4762   gboolean have_rtx = FALSE;
4763   GstStructure *pt_map = NULL;
4764   GstElement *ret = NULL;
4765   GstWebRTCRTPTransceiver *trans;
4766
4767   stream = _find_transport_for_session (webrtc, session_id);
4768   trans = _find_transceiver (webrtc, &session_id,
4769       (FindTransceiverFunc) transceiver_match_for_mline);
4770
4771   if (stream)
4772     have_rtx = transport_stream_get_pt (stream, "RTX") != 0;
4773
4774   GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
4775       " with transport %" GST_PTR_FORMAT " and pt map %" GST_PTR_FORMAT, stream,
4776       trans, pt_map);
4777
4778   if (have_rtx) {
4779     GstElement *rtx;
4780     GstPad *pad;
4781     gchar *name;
4782
4783     if (stream->rtxsend) {
4784       GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
4785       goto out;
4786     }
4787
4788     GST_INFO ("creating AUX sender");
4789     ret = gst_bin_new (NULL);
4790     rtx = gst_element_factory_make ("rtprtxsend", NULL);
4791     g_object_set (rtx, "max-size-packets", 500, NULL);
4792     _set_rtx_ptmap_from_stream (webrtc, stream);
4793
4794     if (WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map)
4795       g_object_set (rtx, "ssrc-map",
4796           WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map, NULL);
4797
4798     gst_bin_add (GST_BIN (ret), rtx);
4799
4800     pad = gst_element_get_static_pad (rtx, "src");
4801     name = g_strdup_printf ("src_%u", session_id);
4802     gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
4803     g_free (name);
4804     gst_object_unref (pad);
4805
4806     pad = gst_element_get_static_pad (rtx, "sink");
4807     name = g_strdup_printf ("sink_%u", session_id);
4808     gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
4809     g_free (name);
4810     gst_object_unref (pad);
4811
4812     stream->rtxsend = gst_object_ref (rtx);
4813   }
4814
4815 out:
4816   if (pt_map)
4817     gst_structure_free (pt_map);
4818
4819   return ret;
4820 }
4821
4822 static GstElement *
4823 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
4824     GstWebRTCBin * webrtc)
4825 {
4826   GstElement *ret = NULL;
4827   GstElement *prev = NULL;
4828   GstPad *sinkpad = NULL;
4829   TransportStream *stream;
4830   gint red_pt = 0;
4831   gint rtx_pt = 0;
4832
4833   stream = _find_transport_for_session (webrtc, session_id);
4834
4835   if (stream) {
4836     red_pt = transport_stream_get_pt (stream, "RED");
4837     rtx_pt = transport_stream_get_pt (stream, "RTX");
4838   }
4839
4840   GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT,
4841       stream);
4842
4843   if (red_pt || rtx_pt)
4844     ret = gst_bin_new (NULL);
4845
4846   if (rtx_pt) {
4847     if (stream->rtxreceive) {
4848       GST_WARNING_OBJECT (webrtc,
4849           "rtprtxreceive already created! rtpbin bug?!");
4850       goto error;
4851     }
4852
4853     stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
4854     _set_rtx_ptmap_from_stream (webrtc, stream);
4855
4856     gst_bin_add (GST_BIN (ret), stream->rtxreceive);
4857
4858     sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink");
4859
4860     prev = gst_object_ref (stream->rtxreceive);
4861   }
4862
4863   if (red_pt) {
4864     GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
4865
4866     GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
4867         red_pt, session_id);
4868
4869     gst_bin_add (GST_BIN (ret), rtpreddec);
4870
4871     g_object_set (rtpreddec, "pt", red_pt, NULL);
4872
4873     if (prev)
4874       gst_element_link (prev, rtpreddec);
4875     else
4876       sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
4877
4878     prev = rtpreddec;
4879   }
4880
4881   if (sinkpad) {
4882     gchar *name = g_strdup_printf ("sink_%u", session_id);
4883     GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
4884     g_free (name);
4885     gst_object_unref (sinkpad);
4886     gst_element_add_pad (ret, ghost);
4887   }
4888
4889   if (prev) {
4890     gchar *name = g_strdup_printf ("src_%u", session_id);
4891     GstPad *srcpad = gst_element_get_static_pad (prev, "src");
4892     GstPad *ghost = gst_ghost_pad_new (name, srcpad);
4893     g_free (name);
4894     gst_object_unref (srcpad);
4895     gst_element_add_pad (ret, ghost);
4896   }
4897
4898 out:
4899   return ret;
4900
4901 error:
4902   if (ret)
4903     gst_object_unref (ret);
4904   goto out;
4905 }
4906
4907 static GstElement *
4908 on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
4909     GstWebRTCBin * webrtc)
4910 {
4911   TransportStream *stream;
4912   GstElement *ret = NULL;
4913   gint pt = 0;
4914   GObject *internal_storage;
4915
4916   stream = _find_transport_for_session (webrtc, session_id);
4917
4918   /* TODO: for now, we only support ulpfec, but once we support
4919    * more algorithms, if the remote may use more than one algorithm,
4920    * we will want to do the following:
4921    *
4922    * + Return a bin here, with the relevant FEC decoders plugged in
4923    *   and their payload type set to 0
4924    * + Enable the decoders by setting the payload type only when
4925    *   we detect it (by connecting to ptdemux:new-payload-type for
4926    *   example)
4927    */
4928   if (stream)
4929     pt = transport_stream_get_pt (stream, "ULPFEC");
4930
4931   if (pt) {
4932     GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
4933         pt, session_id);
4934     ret = gst_element_factory_make ("rtpulpfecdec", NULL);
4935     g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
4936         &internal_storage);
4937
4938     g_object_set (ret, "pt", pt, "storage", internal_storage, NULL);
4939     g_object_unref (internal_storage);
4940   }
4941
4942   return ret;
4943 }
4944
4945 static GstElement *
4946 on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
4947     GstWebRTCBin * webrtc)
4948 {
4949   GstElement *ret = NULL;
4950   GstElement *prev = NULL;
4951   TransportStream *stream;
4952   guint ulpfec_pt = 0;
4953   guint red_pt = 0;
4954   GstPad *sinkpad = NULL;
4955   GstWebRTCRTPTransceiver *trans;
4956
4957   stream = _find_transport_for_session (webrtc, session_id);
4958   trans = _find_transceiver (webrtc, &session_id,
4959       (FindTransceiverFunc) transceiver_match_for_mline);
4960
4961   if (stream) {
4962     ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
4963     red_pt = transport_stream_get_pt (stream, "RED");
4964   }
4965
4966   if (ulpfec_pt || red_pt)
4967     ret = gst_bin_new (NULL);
4968
4969   if (ulpfec_pt) {
4970     GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4971     GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
4972
4973     GST_DEBUG_OBJECT (webrtc,
4974         "Creating ULPFEC encoder for session %d with pt %d", session_id,
4975         ulpfec_pt);
4976
4977     gst_bin_add (GST_BIN (ret), fecenc);
4978     sinkpad = gst_element_get_static_pad (fecenc, "sink");
4979     g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
4980         WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
4981
4982
4983     if (caps && !gst_caps_is_empty (caps)) {
4984       const GstStructure *s = gst_caps_get_structure (caps, 0);
4985       const gchar *media = gst_structure_get_string (s, "media");
4986
4987       if (!g_strcmp0 (media, "video"))
4988         g_object_set (fecenc, "multipacket", TRUE, NULL);
4989     }
4990
4991     prev = fecenc;
4992   }
4993
4994   if (red_pt) {
4995     GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
4996
4997     GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
4998         session_id, red_pt);
4999
5000     gst_bin_add (GST_BIN (ret), redenc);
5001     if (prev)
5002       gst_element_link (prev, redenc);
5003     else
5004       sinkpad = gst_element_get_static_pad (redenc, "sink");
5005
5006     g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
5007
5008     prev = redenc;
5009   }
5010
5011   if (sinkpad) {
5012     GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
5013     gst_object_unref (sinkpad);
5014     gst_element_add_pad (ret, ghost);
5015   }
5016
5017   if (prev) {
5018     GstPad *srcpad = gst_element_get_static_pad (prev, "src");
5019     GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
5020     gst_object_unref (srcpad);
5021     gst_element_add_pad (ret, ghost);
5022   }
5023
5024   return ret;
5025 }
5026
5027 static void
5028 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
5029     GstWebRTCBin * webrtc)
5030 {
5031   GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
5032 }
5033
5034 static void
5035 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
5036     GstWebRTCBin * webrtc)
5037 {
5038   GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
5039 }
5040
5041 static void
5042 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
5043     GstWebRTCBin * webrtc)
5044 {
5045   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
5046       ssrc);
5047 }
5048
5049 static void
5050 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
5051     GstWebRTCBin * webrtc)
5052 {
5053   GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
5054 }
5055
5056 static void
5057 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
5058     GstWebRTCBin * webrtc)
5059 {
5060   GST_INFO_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
5061 }
5062
5063 static void
5064 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
5065     GstWebRTCBin * webrtc)
5066 {
5067   GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
5068 }
5069
5070 static void
5071 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
5072     GstWebRTCBin * webrtc)
5073 {
5074   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
5075 }
5076
5077 static void
5078 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
5079     GstWebRTCBin * webrtc)
5080 {
5081   GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
5082 }
5083
5084 static void
5085 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
5086     GstWebRTCBin * webrtc)
5087 {
5088   GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
5089 }
5090
5091 static void
5092 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
5093     GstWebRTCBin * webrtc)
5094 {
5095   GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
5096       ssrc);
5097 }
5098
5099 static void
5100 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
5101     GstWebRTCBin * webrtc)
5102 {
5103   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
5104       ssrc);
5105 }
5106
5107 static void
5108 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
5109     guint session_id, guint ssrc, GstWebRTCBin * webrtc)
5110 {
5111   GstWebRTCRTPTransceiver *trans;
5112
5113   trans = _find_transceiver (webrtc, &session_id,
5114       (FindTransceiverFunc) transceiver_match_for_mline);
5115
5116   if (trans) {
5117     /* We don't set do-retransmission on rtpbin as we want per-session control */
5118     g_object_set (jitterbuffer, "do-retransmission",
5119         WEBRTC_TRANSCEIVER (trans)->do_nack, NULL);
5120   } else {
5121     g_assert_not_reached ();
5122   }
5123 }
5124
5125 static void
5126 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
5127     guint session_id, GstWebRTCBin * webrtc)
5128 {
5129   /* TODO: when exposing latency, set size-time based on that */
5130   g_object_set (storage, "size-time", (guint64) 250 * GST_MSECOND, NULL);
5131 }
5132
5133 static GstElement *
5134 _create_rtpbin (GstWebRTCBin * webrtc)
5135 {
5136   GstElement *rtpbin;
5137
5138   if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
5139     return NULL;
5140
5141   /* mandated by WebRTC */
5142   gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
5143
5144   g_object_set (rtpbin, "do-lost", TRUE, NULL);
5145
5146   g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
5147       webrtc);
5148   g_signal_connect (rtpbin, "request-pt-map",
5149       G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
5150   g_signal_connect (rtpbin, "request-aux-sender",
5151       G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
5152   g_signal_connect (rtpbin, "request-aux-receiver",
5153       G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
5154   g_signal_connect (rtpbin, "new-storage",
5155       G_CALLBACK (on_rtpbin_new_storage), webrtc);
5156   g_signal_connect (rtpbin, "request-fec-decoder",
5157       G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
5158   g_signal_connect (rtpbin, "request-fec-encoder",
5159       G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
5160   g_signal_connect (rtpbin, "on-bye-ssrc",
5161       G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
5162   g_signal_connect (rtpbin, "on-bye-timeout",
5163       G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
5164   g_signal_connect (rtpbin, "on-new-ssrc",
5165       G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
5166   g_signal_connect (rtpbin, "on-new-sender-ssrc",
5167       G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
5168   g_signal_connect (rtpbin, "on-sender-ssrc-active",
5169       G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
5170   g_signal_connect (rtpbin, "on-sender-timeout",
5171       G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
5172   g_signal_connect (rtpbin, "on-ssrc-active",
5173       G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
5174   g_signal_connect (rtpbin, "on-ssrc-collision",
5175       G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
5176   g_signal_connect (rtpbin, "on-ssrc-sdes",
5177       G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
5178   g_signal_connect (rtpbin, "on-ssrc-validated",
5179       G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
5180   g_signal_connect (rtpbin, "on-timeout",
5181       G_CALLBACK (on_rtpbin_timeout), webrtc);
5182   g_signal_connect (rtpbin, "new-jitterbuffer",
5183       G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
5184
5185   return rtpbin;
5186 }
5187
5188 static GstStateChangeReturn
5189 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
5190 {
5191   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
5192   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
5193
5194   GST_DEBUG ("changing state: %s => %s",
5195       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
5196       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
5197
5198   switch (transition) {
5199     case GST_STATE_CHANGE_NULL_TO_READY:{
5200       if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
5201         return GST_STATE_CHANGE_FAILURE;
5202       _start_thread (webrtc);
5203       PC_LOCK (webrtc);
5204       _update_need_negotiation (webrtc);
5205       PC_UNLOCK (webrtc);
5206       break;
5207     }
5208     case GST_STATE_CHANGE_READY_TO_PAUSED:
5209       webrtc->priv->running = TRUE;
5210       break;
5211     default:
5212       break;
5213   }
5214
5215   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
5216   if (ret == GST_STATE_CHANGE_FAILURE)
5217     return ret;
5218
5219   switch (transition) {
5220     case GST_STATE_CHANGE_READY_TO_PAUSED:
5221       /* Mangle the return value to NO_PREROLL as that's what really is
5222        * occurring here however cannot be propagated correctly due to nicesrc
5223        * requiring that it be in PLAYING already in order to send/receive
5224        * correctly :/ */
5225       ret = GST_STATE_CHANGE_NO_PREROLL;
5226       break;
5227     case GST_STATE_CHANGE_PAUSED_TO_READY:
5228       webrtc->priv->running = FALSE;
5229       break;
5230     case GST_STATE_CHANGE_READY_TO_NULL:
5231       _stop_thread (webrtc);
5232       break;
5233     default:
5234       break;
5235   }
5236
5237   return ret;
5238 }
5239
5240 static GstPadProbeReturn
5241 pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
5242 {
5243   GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
5244
5245   return GST_PAD_PROBE_OK;
5246 }
5247
5248 static GstPad *
5249 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
5250     const gchar * name, const GstCaps * caps)
5251 {
5252   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
5253   GstWebRTCBinPad *pad = NULL;
5254   guint serial;
5255
5256   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
5257     return NULL;
5258
5259   if (templ->direction == GST_PAD_SINK ||
5260       g_strcmp0 (templ->name_template, "sink_%u") == 0) {
5261     GstWebRTCRTPTransceiver *trans;
5262
5263     GST_OBJECT_LOCK (webrtc);
5264     if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
5265       /* no name given when requesting the pad, use next available int */
5266       serial = webrtc->priv->max_sink_pad_serial++;
5267     } else {
5268       /* parse serial number from requested padname */
5269       serial = g_ascii_strtoull (&name[5], NULL, 10);
5270       if (serial > webrtc->priv->max_sink_pad_serial)
5271         webrtc->priv->max_sink_pad_serial = serial;
5272     }
5273     GST_OBJECT_UNLOCK (webrtc);
5274
5275     pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial);
5276     trans = _find_transceiver_for_mline (webrtc, serial);
5277     if (!trans)
5278       trans =
5279           GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
5280               GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, serial));
5281     pad->trans = gst_object_ref (trans);
5282
5283     pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
5284         GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
5285         (GstPadProbeCallback) pad_block, NULL, NULL);
5286     webrtc->priv->pending_sink_transceivers =
5287         g_list_append (webrtc->priv->pending_sink_transceivers,
5288         gst_object_ref (pad));
5289     _add_pad (webrtc, pad);
5290   }
5291
5292   return GST_PAD (pad);
5293 }
5294
5295 static void
5296 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
5297 {
5298   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
5299   GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
5300
5301   if (webrtc_pad->trans)
5302     gst_object_unref (webrtc_pad->trans);
5303   webrtc_pad->trans = NULL;
5304
5305   _remove_pad (webrtc, webrtc_pad);
5306   PC_LOCK (webrtc);
5307   _update_need_negotiation (webrtc);
5308   PC_UNLOCK (webrtc);
5309 }
5310
5311 static void
5312 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
5313     const GValue * value, GParamSpec * pspec)
5314 {
5315   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5316
5317   switch (prop_id) {
5318     case PROP_STUN_SERVER:
5319     case PROP_TURN_SERVER:
5320       g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
5321       break;
5322     case PROP_BUNDLE_POLICY:
5323       if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
5324         GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
5325       } else {
5326         webrtc->bundle_policy = g_value_get_enum (value);
5327       }
5328       break;
5329     case PROP_ICE_TRANSPORT_POLICY:
5330       webrtc->ice_transport_policy = g_value_get_enum (value);
5331       g_object_set (webrtc->priv->ice, "force-relay",
5332           webrtc->ice_transport_policy ==
5333           GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE, NULL);
5334       break;
5335     default:
5336       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5337       break;
5338   }
5339 }
5340
5341 static void
5342 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
5343     GValue * value, GParamSpec * pspec)
5344 {
5345   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5346
5347   PC_LOCK (webrtc);
5348   switch (prop_id) {
5349     case PROP_CONNECTION_STATE:
5350       g_value_set_enum (value, webrtc->peer_connection_state);
5351       break;
5352     case PROP_SIGNALING_STATE:
5353       g_value_set_enum (value, webrtc->signaling_state);
5354       break;
5355     case PROP_ICE_GATHERING_STATE:
5356       g_value_set_enum (value, webrtc->ice_gathering_state);
5357       break;
5358     case PROP_ICE_CONNECTION_STATE:
5359       g_value_set_enum (value, webrtc->ice_connection_state);
5360       break;
5361     case PROP_LOCAL_DESCRIPTION:
5362       if (webrtc->pending_local_description)
5363         g_value_set_boxed (value, webrtc->pending_local_description);
5364       else if (webrtc->current_local_description)
5365         g_value_set_boxed (value, webrtc->current_local_description);
5366       else
5367         g_value_set_boxed (value, NULL);
5368       break;
5369     case PROP_CURRENT_LOCAL_DESCRIPTION:
5370       g_value_set_boxed (value, webrtc->current_local_description);
5371       break;
5372     case PROP_PENDING_LOCAL_DESCRIPTION:
5373       g_value_set_boxed (value, webrtc->pending_local_description);
5374       break;
5375     case PROP_REMOTE_DESCRIPTION:
5376       if (webrtc->pending_remote_description)
5377         g_value_set_boxed (value, webrtc->pending_remote_description);
5378       else if (webrtc->current_remote_description)
5379         g_value_set_boxed (value, webrtc->current_remote_description);
5380       else
5381         g_value_set_boxed (value, NULL);
5382       break;
5383     case PROP_CURRENT_REMOTE_DESCRIPTION:
5384       g_value_set_boxed (value, webrtc->current_remote_description);
5385       break;
5386     case PROP_PENDING_REMOTE_DESCRIPTION:
5387       g_value_set_boxed (value, webrtc->pending_remote_description);
5388       break;
5389     case PROP_STUN_SERVER:
5390     case PROP_TURN_SERVER:
5391       g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
5392       break;
5393     case PROP_BUNDLE_POLICY:
5394       g_value_set_enum (value, webrtc->bundle_policy);
5395       break;
5396     case PROP_ICE_TRANSPORT_POLICY:
5397       g_value_set_enum (value, webrtc->ice_transport_policy);
5398       break;
5399     default:
5400       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
5401       break;
5402   }
5403   PC_UNLOCK (webrtc);
5404 }
5405
5406 static void
5407 _free_pending_pad (GstPad * pad)
5408 {
5409   gst_object_unref (pad);
5410 }
5411
5412 static void
5413 gst_webrtc_bin_dispose (GObject * object)
5414 {
5415   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5416
5417   if (webrtc->priv->ice)
5418     gst_object_unref (webrtc->priv->ice);
5419   webrtc->priv->ice = NULL;
5420
5421   if (webrtc->priv->ice_stream_map)
5422     g_array_free (webrtc->priv->ice_stream_map, TRUE);
5423   webrtc->priv->ice_stream_map = NULL;
5424
5425   g_clear_object (&webrtc->priv->sctp_transport);
5426
5427   G_OBJECT_CLASS (parent_class)->dispose (object);
5428 }
5429
5430 static void
5431 gst_webrtc_bin_finalize (GObject * object)
5432 {
5433   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
5434
5435   if (webrtc->priv->transports)
5436     g_array_free (webrtc->priv->transports, TRUE);
5437   webrtc->priv->transports = NULL;
5438
5439   if (webrtc->priv->transceivers)
5440     g_array_free (webrtc->priv->transceivers, TRUE);
5441   webrtc->priv->transceivers = NULL;
5442
5443   if (webrtc->priv->data_channels)
5444     g_array_free (webrtc->priv->data_channels, TRUE);
5445   webrtc->priv->data_channels = NULL;
5446
5447   if (webrtc->priv->pending_data_channels)
5448     g_array_free (webrtc->priv->pending_data_channels, TRUE);
5449   webrtc->priv->pending_data_channels = NULL;
5450
5451   if (webrtc->priv->pending_ice_candidates)
5452     g_array_free (webrtc->priv->pending_ice_candidates, TRUE);
5453   webrtc->priv->pending_ice_candidates = NULL;
5454
5455   if (webrtc->priv->session_mid_map)
5456     g_array_free (webrtc->priv->session_mid_map, TRUE);
5457   webrtc->priv->session_mid_map = NULL;
5458
5459   if (webrtc->priv->pending_pads)
5460     g_list_free_full (webrtc->priv->pending_pads,
5461         (GDestroyNotify) _free_pending_pad);
5462   webrtc->priv->pending_pads = NULL;
5463
5464   if (webrtc->priv->pending_sink_transceivers)
5465     g_list_free_full (webrtc->priv->pending_sink_transceivers,
5466         (GDestroyNotify) gst_object_unref);
5467   webrtc->priv->pending_sink_transceivers = NULL;
5468
5469   if (webrtc->current_local_description)
5470     gst_webrtc_session_description_free (webrtc->current_local_description);
5471   webrtc->current_local_description = NULL;
5472   if (webrtc->pending_local_description)
5473     gst_webrtc_session_description_free (webrtc->pending_local_description);
5474   webrtc->pending_local_description = NULL;
5475
5476   if (webrtc->current_remote_description)
5477     gst_webrtc_session_description_free (webrtc->current_remote_description);
5478   webrtc->current_remote_description = NULL;
5479   if (webrtc->pending_remote_description)
5480     gst_webrtc_session_description_free (webrtc->pending_remote_description);
5481   webrtc->pending_remote_description = NULL;
5482
5483   if (webrtc->priv->last_generated_answer)
5484     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
5485   webrtc->priv->last_generated_answer = NULL;
5486   if (webrtc->priv->last_generated_offer)
5487     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
5488   webrtc->priv->last_generated_offer = NULL;
5489
5490   if (webrtc->priv->stats)
5491     gst_structure_free (webrtc->priv->stats);
5492   webrtc->priv->stats = NULL;
5493
5494   g_mutex_clear (PC_GET_LOCK (webrtc));
5495   g_cond_clear (PC_GET_COND (webrtc));
5496
5497   G_OBJECT_CLASS (parent_class)->finalize (object);
5498 }
5499
5500 static void
5501 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
5502 {
5503   GObjectClass *gobject_class = (GObjectClass *) klass;
5504   GstElementClass *element_class = (GstElementClass *) klass;
5505
5506   element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
5507   element_class->release_pad = gst_webrtc_bin_release_pad;
5508   element_class->change_state = gst_webrtc_bin_change_state;
5509
5510   gst_element_class_add_static_pad_template_with_gtype (element_class,
5511       &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
5512   gst_element_class_add_static_pad_template (element_class, &src_template);
5513
5514   gst_element_class_set_metadata (element_class, "WebRTC Bin",
5515       "Filter/Network/WebRTC", "A bin for webrtc connections",
5516       "Matthew Waters <matthew@centricular.com>");
5517
5518   gobject_class->get_property = gst_webrtc_bin_get_property;
5519   gobject_class->set_property = gst_webrtc_bin_set_property;
5520   gobject_class->dispose = gst_webrtc_bin_dispose;
5521   gobject_class->finalize = gst_webrtc_bin_finalize;
5522
5523   g_object_class_install_property (gobject_class,
5524       PROP_LOCAL_DESCRIPTION,
5525       g_param_spec_boxed ("local-description", "Local Description",
5526           "The local SDP description in use for this connection. "
5527           "Favours a pending description over the current description",
5528           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5529           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5530
5531   g_object_class_install_property (gobject_class,
5532       PROP_CURRENT_LOCAL_DESCRIPTION,
5533       g_param_spec_boxed ("current-local-description",
5534           "Current Local Description",
5535           "The local description that was successfully negotiated the last time "
5536           "the connection transitioned into the stable state",
5537           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5538           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5539
5540   g_object_class_install_property (gobject_class,
5541       PROP_PENDING_LOCAL_DESCRIPTION,
5542       g_param_spec_boxed ("pending-local-description",
5543           "Pending Local Description",
5544           "The local description that is in the process of being negotiated plus "
5545           "any local candidates that have been generated by the ICE Agent since the "
5546           "offer or answer was created",
5547           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5548           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5549
5550   g_object_class_install_property (gobject_class,
5551       PROP_REMOTE_DESCRIPTION,
5552       g_param_spec_boxed ("remote-description", "Remote Description",
5553           "The remote SDP description to use for this connection. "
5554           "Favours a pending description over the current description",
5555           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5556           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5557
5558   g_object_class_install_property (gobject_class,
5559       PROP_CURRENT_REMOTE_DESCRIPTION,
5560       g_param_spec_boxed ("current-remote-description",
5561           "Current Remote Description",
5562           "The last remote description that was successfully negotiated the last "
5563           "time the connection transitioned into the stable state plus any remote "
5564           "candidates that have been supplied via addIceCandidate() since the offer "
5565           "or answer was created",
5566           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5567           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5568
5569   g_object_class_install_property (gobject_class,
5570       PROP_PENDING_REMOTE_DESCRIPTION,
5571       g_param_spec_boxed ("pending-remote-description",
5572           "Pending Remote Description",
5573           "The remote description that is in the process of being negotiated, "
5574           "complete with any remote candidates that have been supplied via "
5575           "addIceCandidate() since the offer or answer was created",
5576           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
5577           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5578
5579   g_object_class_install_property (gobject_class,
5580       PROP_STUN_SERVER,
5581       g_param_spec_string ("stun-server", "STUN Server",
5582           "The STUN server of the form stun://hostname:port",
5583           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5584
5585   g_object_class_install_property (gobject_class,
5586       PROP_TURN_SERVER,
5587       g_param_spec_string ("turn-server", "TURN Server",
5588           "The TURN server of the form turn(s)://username:password@host:port. "
5589           "This is a convenience property, use #GstWebRTCBin::add-turn-server "
5590           "if you wish to use multiple TURN servers",
5591           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5592
5593   g_object_class_install_property (gobject_class,
5594       PROP_CONNECTION_STATE,
5595       g_param_spec_enum ("connection-state", "Connection State",
5596           "The overall connection state of this element",
5597           GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
5598           GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
5599           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5600
5601   g_object_class_install_property (gobject_class,
5602       PROP_SIGNALING_STATE,
5603       g_param_spec_enum ("signaling-state", "Signaling State",
5604           "The signaling state of this element",
5605           GST_TYPE_WEBRTC_SIGNALING_STATE,
5606           GST_WEBRTC_SIGNALING_STATE_STABLE,
5607           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5608
5609   g_object_class_install_property (gobject_class,
5610       PROP_ICE_CONNECTION_STATE,
5611       g_param_spec_enum ("ice-connection-state", "ICE connection state",
5612           "The collective connection state of all ICETransport's",
5613           GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
5614           GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
5615           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5616
5617   g_object_class_install_property (gobject_class,
5618       PROP_ICE_GATHERING_STATE,
5619       g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
5620           "The collective gathering state of all ICETransport's",
5621           GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
5622           GST_WEBRTC_ICE_GATHERING_STATE_NEW,
5623           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5624
5625   g_object_class_install_property (gobject_class,
5626       PROP_BUNDLE_POLICY,
5627       g_param_spec_enum ("bundle-policy", "Bundle Policy",
5628           "The policy to apply for bundling",
5629           GST_TYPE_WEBRTC_BUNDLE_POLICY,
5630           GST_WEBRTC_BUNDLE_POLICY_NONE,
5631           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5632
5633   g_object_class_install_property (gobject_class,
5634       PROP_ICE_TRANSPORT_POLICY,
5635       g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
5636           "The policy to apply for ICE transport",
5637           GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
5638           GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
5639           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5640
5641   /**
5642    * GstWebRTCBin::create-offer:
5643    * @object: the #webrtcbin
5644    * @options: (nullable): create-offer options
5645    * @promise: a #GstPromise which will contain the offer
5646    */
5647   gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
5648       g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
5649       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5650       G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL,
5651       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
5652       GST_TYPE_PROMISE);
5653
5654   /**
5655    * GstWebRTCBin::create-answer:
5656    * @object: the #webrtcbin
5657    * @options: (nullable): create-answer options
5658    * @promise: a #GstPromise which will contain the answer
5659    */
5660   gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
5661       g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
5662       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5663       G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL,
5664       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
5665       GST_TYPE_PROMISE);
5666
5667   /**
5668    * GstWebRTCBin::set-local-description:
5669    * @object: the #GstWebRTCBin
5670    * @desc: a #GstWebRTCSessionDescription description
5671    * @promise: (nullable): a #GstPromise to be notified when it's set
5672    */
5673   gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
5674       g_signal_new_class_handler ("set-local-description",
5675       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5676       G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL,
5677       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
5678       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
5679
5680   /**
5681    * GstWebRTCBin::set-remote-description:
5682    * @object: the #GstWebRTCBin
5683    * @desc: a #GstWebRTCSessionDescription description
5684    * @promise: (nullable): a #GstPromise to be notified when it's set
5685    */
5686   gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
5687       g_signal_new_class_handler ("set-remote-description",
5688       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5689       G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL,
5690       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
5691       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
5692
5693   /**
5694    * GstWebRTCBin::add-ice-candidate:
5695    * @object: the #webrtcbin
5696    * @mline_index: the index of the media description in the SDP
5697    * @ice-candidate: an ice candidate
5698    */
5699   gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
5700       g_signal_new_class_handler ("add-ice-candidate",
5701       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5702       G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL,
5703       g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
5704
5705   /**
5706    * GstWebRTCBin::get-stats:
5707    * @object: the #webrtcbin
5708    * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
5709    * @promise: a #GstPromise for the result
5710    *
5711    * The @promise will contain the result of retrieving the session statistics.
5712    * The structure will be named 'application/x-webrtc-stats and contain the
5713    * following based on the webrtc-stats spec available from
5714    * https://www.w3.org/TR/webrtc-stats/.  As the webrtc-stats spec is a draft
5715    * and is constantly changing these statistics may be changed to fit with
5716    * the latest spec.
5717    *
5718    * Each field key is a unique identifer for each RTCStats
5719    * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
5720    * GstStructure) in the RTCStatsReport
5721    * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object).  Each supported
5722    * field in the RTCStats subclass is outlined below.
5723    *
5724    * Each statistics structure contains the following values as defined by
5725    * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
5726    *
5727    *  "timestamp"           G_TYPE_DOUBLE               timestamp the statistics were generated
5728    *  "type"                GST_TYPE_WEBRTC_STATS_TYPE  the type of statistics reported
5729    *  "id"                  G_TYPE_STRING               unique identifier
5730    *
5731    * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
5732    *
5733    *  "payload-type"        G_TYPE_UINT                 the rtp payload number in use
5734    *  "clock-rate"          G_TYPE_UINT                 the rtp clock-rate
5735    *
5736    * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
5737    *
5738    *  "ssrc"                G_TYPE_STRING               the rtp sequence src in use
5739    *  "transport-id"        G_TYPE_STRING               identifier for the associated RTCTransportStats for this stream
5740    *  "codec-id"            G_TYPE_STRING               identifier for the associated RTCCodecStats for this stream
5741    *  "fir-count"           G_TYPE_UINT                 FIR requests received by the sender (only for local statistics)
5742    *  "pli-count"           G_TYPE_UINT                 PLI requests received by the sender (only for local statistics)
5743    *  "nack-count"          G_TYPE_UINT                 NACK requests received by the sender (only for local statistics)
5744    *
5745    * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
5746    *
5747    *  "packets-received"     G_TYPE_UINT64              number of packets received (only for local inbound)
5748    *  "bytes-received"       G_TYPE_UINT64              number of bytes received (only for local inbound)
5749    *  "packets-lost"         G_TYPE_UINT                number of packets lost
5750    *  "jitter"               G_TYPE_DOUBLE              packet jitter measured in secondss
5751    *
5752    * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
5753    *
5754    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteOutboundRTPStreamStats
5755    *
5756    * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
5757    *
5758    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCOutboundRTPSTreamStats
5759    *  "round-trip-time"     G_TYPE_DOUBLE               round trip time of packets measured in seconds
5760    *
5761    * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
5762    *
5763    *  "packets-sent"        G_TYPE_UINT64               number of packets sent (only for local outbound)
5764    *  "bytes-sent"          G_TYPE_UINT64               number of packets sent (only for local outbound)
5765    *
5766    * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
5767    *
5768    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteInboundRTPSTreamStats
5769    *
5770    * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
5771    *
5772    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCInboundRTPSTreamStats
5773    *
5774    */
5775   gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
5776       g_signal_new_class_handler ("get-stats",
5777       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5778       G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL,
5779       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_PAD,
5780       GST_TYPE_PROMISE);
5781
5782   /**
5783    * GstWebRTCBin::on-negotiation-needed:
5784    * @object: the #webrtcbin
5785    */
5786   gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
5787       g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
5788       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5789       G_TYPE_NONE, 0);
5790
5791   /**
5792    * GstWebRTCBin::on-ice-candidate:
5793    * @object: the #webrtcbin
5794    * @mline_index: the index of the media description in the SDP
5795    * @candidate: the ICE candidate
5796    */
5797   gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
5798       g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
5799       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5800       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
5801
5802   /**
5803    * GstWebRTCBin::on-new-transceiver:
5804    * @object: the #webrtcbin
5805    * @candidate: the new #GstWebRTCRTPTransceiver
5806    */
5807   gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
5808       g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
5809       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5810       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
5811
5812   /**
5813    * GstWebRTCBin::on-data-channel:
5814    * @object: the #GstWebRTCBin
5815    * @candidate: the new `GstWebRTCDataChannel`
5816    */
5817   gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
5818       g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
5819       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5820       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
5821
5822   /**
5823    * GstWebRTCBin::add-transceiver:
5824    * @object: the #webrtcbin
5825    * @direction: the direction of the new transceiver
5826    * @caps: (allow none): the codec preferences for this transceiver
5827    *
5828    * Returns: the new #GstWebRTCRTPTransceiver
5829    */
5830   gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
5831       g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
5832       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5833       G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
5834       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
5835       GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
5836
5837   /**
5838    * GstWebRTCBin::get-transceivers:
5839    * @object: the #webrtcbin
5840    *
5841    * Returns: a #GArray of #GstWebRTCRTPTransceivers
5842    */
5843   gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
5844       g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
5845       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5846       G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL,
5847       g_cclosure_marshal_generic, G_TYPE_ARRAY, 0);
5848
5849   /**
5850    * GstWebRTCBin::get-transceiver:
5851    * @object: the #GstWebRTCBin
5852    * @idx: The index of the transceiver
5853    *
5854    * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
5855    * Since: 1.16
5856    */
5857   gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
5858       g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
5859       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5860       G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL,
5861       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1,
5862       G_TYPE_INT);
5863
5864   /**
5865    * GstWebRTCBin::add-turn-server:
5866    * @object: the #GstWebRTCBin
5867    * @uri: The uri of the server of the form turn(s)://username:password@host:port
5868    *
5869    * Add a turn server to obtain ICE candidates from
5870    */
5871   gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
5872       g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
5873       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5874       G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL,
5875       g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
5876
5877   /*
5878    * GstWebRTCBin::create-data-channel:
5879    * @object: the #GstWebRTCBin
5880    * @label: the label for the data channel
5881    * @options: a #GstStructure of options for creating the data channel
5882    *
5883    * The options dictionary is the same format as the RTCDataChannelInit
5884    * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
5885    * and reproduced below
5886    *
5887    *  ordered               G_TYPE_BOOLEAN        Whether the channal will send data with guarenteed ordering
5888    *  max-packet-lifetime   G_TYPE_INT            The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
5889    *  max-retransmits       G_TYPE_INT            The number of times data will be attempted to be transmitted without acknowledgement before dropping
5890    *  protocol              G_TYPE_STRING         The subprotocol used by this channel
5891    *  negotiated            G_TYPE_BOOLEAN        Whether the created data channel should not perform in-band chnanel announcment.  If %TRUE, then application must negotiate the channel itself and create the corresponding channel on the peer with the same id.
5892    *  id                    G_TYPE_INT            Override the default identifier selection of this channel
5893    *  priority              GST_TYPE_WEBRTC_PRIORITY_TYPE   The priority to use for this channel
5894    *
5895    * Returns: (transfer full): a new data channel object
5896    */
5897   gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
5898       g_signal_new_class_handler ("create-data-channel",
5899       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5900       G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
5901       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_DATA_CHANNEL, 2,
5902       G_TYPE_STRING, GST_TYPE_STRUCTURE);
5903 }
5904
5905 static void
5906 _deref_unparent_and_unref (GObject ** object)
5907 {
5908   GstObject *obj = GST_OBJECT (*object);
5909
5910   GST_OBJECT_PARENT (obj) = NULL;
5911
5912   gst_object_unref (*object);
5913 }
5914
5915 static void
5916 _transport_free (GObject ** object)
5917 {
5918   TransportStream *stream = (TransportStream *) * object;
5919   GstWebRTCBin *webrtc;
5920
5921   webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
5922
5923   if (stream->transport) {
5924     g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
5925     g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
5926   }
5927   if (stream->rtcp_transport) {
5928     g_signal_handlers_disconnect_by_data (stream->rtcp_transport->transport,
5929         webrtc);
5930     g_signal_handlers_disconnect_by_data (stream->rtcp_transport, webrtc);
5931   }
5932
5933   gst_object_unref (*object);
5934 }
5935
5936 static void
5937 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
5938 {
5939   webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
5940   g_mutex_init (PC_GET_LOCK (webrtc));
5941   g_cond_init (PC_GET_COND (webrtc));
5942
5943   webrtc->rtpbin = _create_rtpbin (webrtc);
5944   gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
5945
5946   webrtc->priv->transceivers = g_array_new (FALSE, TRUE, sizeof (gpointer));
5947   g_array_set_clear_func (webrtc->priv->transceivers,
5948       (GDestroyNotify) _deref_unparent_and_unref);
5949
5950   webrtc->priv->transports = g_array_new (FALSE, TRUE, sizeof (gpointer));
5951   g_array_set_clear_func (webrtc->priv->transports,
5952       (GDestroyNotify) _transport_free);
5953
5954   webrtc->priv->data_channels = g_array_new (FALSE, TRUE, sizeof (gpointer));
5955   g_array_set_clear_func (webrtc->priv->data_channels,
5956       (GDestroyNotify) _deref_and_unref);
5957
5958   webrtc->priv->pending_data_channels =
5959       g_array_new (FALSE, TRUE, sizeof (gpointer));
5960   g_array_set_clear_func (webrtc->priv->pending_data_channels,
5961       (GDestroyNotify) _deref_and_unref);
5962
5963   webrtc->priv->session_mid_map =
5964       g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
5965   g_array_set_clear_func (webrtc->priv->session_mid_map,
5966       (GDestroyNotify) clear_session_mid_item);
5967
5968   webrtc->priv->ice = gst_webrtc_ice_new ();
5969   g_signal_connect (webrtc->priv->ice, "on-ice-candidate",
5970       G_CALLBACK (_on_ice_candidate), webrtc);
5971   webrtc->priv->ice_stream_map =
5972       g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
5973   webrtc->priv->pending_ice_candidates =
5974       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem *));
5975   g_array_set_clear_func (webrtc->priv->pending_ice_candidates,
5976       (GDestroyNotify) _clear_ice_candidate_item);
5977
5978   /* we start off closed until we move to READY */
5979   webrtc->priv->is_closed = TRUE;
5980 }