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