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