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