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