webrtc: Fix docs for create-data-channel action signal
[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     if (max_channels <= 0) {
7114       max_channels = 65534;
7115     }
7116
7117     g_return_val_if_fail (id <= max_channels, NULL);
7118   }
7119
7120   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
7121       !_have_sctp_elements (webrtc))
7122     return NULL;
7123
7124   PC_LOCK (webrtc);
7125   DC_LOCK (webrtc);
7126   /* check if the id has been used already */
7127   if (id != -1) {
7128     WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
7129     if (channel) {
7130       GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
7131           ("Attempting to add a data channel with a duplicate ID: %i", id),
7132           NULL);
7133       DC_UNLOCK (webrtc);
7134       PC_UNLOCK (webrtc);
7135       return NULL;
7136     }
7137   } else if (webrtc->current_local_description
7138       && webrtc->current_remote_description && webrtc->priv->sctp_transport
7139       && webrtc->priv->sctp_transport->transport) {
7140     /* else we can only generate an id if we're configured already.  The other
7141      * case for generating an id is on sdp setting */
7142     id = _generate_data_channel_id (webrtc);
7143     if (id == -1) {
7144       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
7145           ("%s", "Failed to generate an identifier for a data channel"), NULL);
7146       DC_UNLOCK (webrtc);
7147       PC_UNLOCK (webrtc);
7148       return NULL;
7149     }
7150   }
7151
7152   ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label,
7153       "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
7154       "max-retransmits", max_retransmits, "protocol", protocol,
7155       "negotiated", negotiated, "id", id, "priority", priority, NULL);
7156
7157   if (!ret) {
7158     DC_UNLOCK (webrtc);
7159     PC_UNLOCK (webrtc);
7160     return ret;
7161   }
7162
7163   g_signal_emit (webrtc, gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL], 0,
7164       ret, TRUE);
7165
7166   gst_bin_add (GST_BIN (webrtc), ret->src_bin);
7167   gst_bin_add (GST_BIN (webrtc), ret->sink_bin);
7168
7169   gst_element_sync_state_with_parent (ret->src_bin);
7170   gst_element_sync_state_with_parent (ret->sink_bin);
7171
7172   ret = gst_object_ref (ret);
7173   webrtc_data_channel_set_webrtcbin (ret, webrtc);
7174   g_ptr_array_add (webrtc->priv->data_channels, ret);
7175   DC_UNLOCK (webrtc);
7176
7177   gst_webrtc_bin_update_sctp_priority (webrtc);
7178   webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
7179   if (webrtc->priv->sctp_transport &&
7180       webrtc->priv->sctp_transport->association_established
7181       && !ret->parent.negotiated) {
7182     webrtc_data_channel_start_negotiation (ret);
7183   } else {
7184     _update_need_negotiation (webrtc);
7185   }
7186
7187   PC_UNLOCK (webrtc);
7188   return ret;
7189 }
7190
7191 /* === rtpbin signal implementations === */
7192
7193 static void
7194 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
7195     GstWebRTCBin * webrtc)
7196 {
7197   gchar *new_pad_name = NULL;
7198
7199   new_pad_name = gst_pad_get_name (new_pad);
7200   GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
7201   if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
7202     guint32 session_id = 0, ssrc = 0, pt = 0;
7203     SsrcMapItem *mid_entry;
7204     GstWebRTCRTPTransceiver *rtp_trans = NULL;
7205     WebRTCTransceiver *trans;
7206     TransportStream *stream;
7207     GstWebRTCBinPad *pad;
7208     guint media_idx;
7209
7210     if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
7211             &pt) != 3) {
7212       g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
7213       return;
7214     }
7215
7216     media_idx = session_id;
7217
7218     PC_LOCK (webrtc);
7219     stream = _find_transport_for_session (webrtc, session_id);
7220     if (!stream)
7221       g_warn_if_reached ();
7222
7223     mid_entry =
7224         find_mid_ssrc_for_ssrc (webrtc,
7225         GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc);
7226
7227     if (mid_entry) {
7228       if (mid_entry->mid) {
7229         /* Can't use the mid_entry if the mid doesn't exist */
7230         rtp_trans = _find_transceiver_for_mid (webrtc, mid_entry->mid);
7231         if (rtp_trans) {
7232           g_assert_cmpint (rtp_trans->mline, ==, mid_entry->media_idx);
7233         }
7234       }
7235
7236       if (mid_entry->media_idx != -1)
7237         media_idx = mid_entry->media_idx;
7238     } else {
7239       GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc);
7240       /* TODO: connect up to fakesink and reconnect later when this information
7241        * is known from RTCP SDES or RTP Header extension
7242        */
7243     }
7244
7245     if (!rtp_trans)
7246       rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
7247     if (!rtp_trans)
7248       g_warn_if_reached ();
7249     trans = WEBRTC_TRANSCEIVER (rtp_trans);
7250     g_assert (trans->stream == stream);
7251
7252     pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
7253     GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
7254         " for rtpbin pad name %s", pad, new_pad_name);
7255     if (!_remove_pending_pad (webrtc, pad)) {
7256       /* assumption here is that rtpbin doesn't duplicate pads and that if
7257        * there is no pending pad, this is a duplicate stream for e.g. simulcast
7258        * or somesuch */
7259       gst_clear_object (&pad);
7260       pad =
7261           _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, rtp_trans, G_MAXUINT,
7262           NULL);
7263       GST_TRACE_OBJECT (webrtc,
7264           "duplicate output ssrc? created new pad %" GST_PTR_FORMAT " for %"
7265           GST_PTR_FORMAT " for rtp pad %s", pad, rtp_trans, new_pad_name);
7266       gst_object_ref_sink (pad);
7267     }
7268
7269     if (!pad)
7270       g_warn_if_reached ();
7271     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
7272
7273     if (webrtc->priv->running)
7274       gst_pad_set_active (GST_PAD (pad), TRUE);
7275
7276     PC_UNLOCK (webrtc);
7277
7278     gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
7279     gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
7280
7281     gst_object_unref (pad);
7282   }
7283   g_free (new_pad_name);
7284 }
7285
7286 /* only used for the receiving streams */
7287 static GstCaps *
7288 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
7289     GstWebRTCBin * webrtc)
7290 {
7291   TransportStream *stream;
7292   GstCaps *ret;
7293
7294   GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
7295       session_id);
7296
7297   PC_LOCK (webrtc);
7298   stream = _find_transport_for_session (webrtc, session_id);
7299   if (!stream)
7300     goto unknown_session;
7301
7302   if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
7303     gst_caps_ref (ret);
7304
7305   GST_DEBUG_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
7306       "session %d", ret, pt, session_id);
7307
7308   PC_UNLOCK (webrtc);
7309   return ret;
7310
7311 unknown_session:
7312   {
7313     PC_UNLOCK (webrtc);
7314     GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
7315     return NULL;
7316   }
7317 }
7318
7319 static GstElement *
7320 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
7321     GstWebRTCBin * webrtc)
7322 {
7323   TransportStream *stream;
7324   GstElement *ret, *rtx;
7325   GstPad *pad;
7326   char *name;
7327   GstElement *aux_sender = NULL;
7328
7329   stream = _find_transport_for_session (webrtc, session_id);
7330   if (!stream) {
7331     /* a rtp session without a stream is a webrtcbin bug */
7332     g_warn_if_reached ();
7333     return NULL;
7334   }
7335
7336   if (stream->rtxsend) {
7337     GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!");
7338     g_warn_if_reached ();
7339     return NULL;
7340   }
7341
7342   GST_DEBUG_OBJECT (webrtc, "requesting aux sender for session %u "
7343       "stream %" GST_PTR_FORMAT, session_id, stream);
7344
7345   ret = gst_bin_new (NULL);
7346   rtx = gst_element_factory_make ("rtprtxsend", NULL);
7347   /* XXX: allow control from outside? */
7348   g_object_set (rtx, "max-size-packets", 500, NULL);
7349
7350   if (!gst_bin_add (GST_BIN (ret), rtx))
7351     g_warn_if_reached ();
7352   ensure_rtx_hdr_ext (stream);
7353
7354   stream->rtxsend = gst_object_ref (rtx);
7355   _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7356
7357   name = g_strdup_printf ("src_%u", session_id);
7358   pad = gst_element_get_static_pad (rtx, "src");
7359
7360
7361   g_signal_emit (webrtc, gst_webrtc_bin_signals[REQUEST_AUX_SENDER], 0,
7362       stream->transport, &aux_sender);
7363   if (aux_sender) {
7364     GstPadLinkReturn link_res;
7365     GstPad *sinkpad = gst_element_get_static_pad (aux_sender, "sink");
7366     GstPad *srcpad = gst_element_get_static_pad (aux_sender, "src");
7367
7368     gst_object_ref_sink (aux_sender);
7369
7370     if (!sinkpad || !srcpad) {
7371       GST_ERROR_OBJECT (webrtc,
7372           "Invalid pads for the aux sender %" GST_PTR_FORMAT
7373           ". Skipping it.", aux_sender);
7374       goto bwe_done;
7375     }
7376
7377     if (!gst_bin_add (GST_BIN (ret), aux_sender)) {
7378       GST_ERROR_OBJECT (webrtc,
7379           "Could not add aux sender %" GST_PTR_FORMAT, aux_sender);
7380       goto bwe_done;
7381     }
7382
7383     link_res = gst_pad_link (pad, sinkpad);
7384     if (link_res != GST_PAD_LINK_OK) {
7385       GST_ERROR_OBJECT (webrtc,
7386           "Could not link aux sender %" GST_PTR_FORMAT " %s", aux_sender,
7387           gst_pad_link_get_name (link_res));
7388       goto bwe_done;
7389     }
7390
7391     gst_clear_object (&pad);
7392     pad = gst_object_ref (srcpad);
7393
7394   bwe_done:
7395     if (pad != srcpad) {
7396       /* Failed using the provided aux sender */
7397       if (gst_object_has_as_parent (GST_OBJECT (aux_sender), GST_OBJECT (ret))) {
7398         gst_bin_remove (GST_BIN (ret), aux_sender);
7399       }
7400     }
7401     gst_clear_object (&aux_sender);
7402     gst_clear_object (&srcpad);
7403     gst_clear_object (&sinkpad);
7404   }
7405
7406   if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7407     g_warn_if_reached ();
7408   gst_clear_object (&pad);
7409   g_clear_pointer (&name, g_free);
7410
7411   name = g_strdup_printf ("sink_%u", session_id);
7412   pad = gst_element_get_static_pad (rtx, "sink");
7413   if (!gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)))
7414     g_warn_if_reached ();
7415   gst_clear_object (&pad);
7416   g_clear_pointer (&name, g_free);
7417
7418   return ret;
7419 }
7420
7421 static GstElement *
7422 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
7423     GstWebRTCBin * webrtc)
7424 {
7425   TransportStream *stream;
7426   GstPad *pad, *ghost;
7427   GstElement *ret;
7428   char *name;
7429
7430   stream = _find_transport_for_session (webrtc, session_id);
7431   if (!stream) {
7432     /* no transport stream before the session has been created is a webrtcbin
7433      * programming error! */
7434     g_warn_if_reached ();
7435     return NULL;
7436   }
7437
7438   if (stream->rtxreceive) {
7439     GST_WARNING_OBJECT (webrtc, "rtprtxreceive already created! rtpbin bug?!");
7440     g_warn_if_reached ();
7441     return NULL;
7442   }
7443
7444   if (stream->reddec) {
7445     GST_WARNING_OBJECT (webrtc, "rtpreddec already created! rtpbin bug?!");
7446     g_warn_if_reached ();
7447     return NULL;
7448   }
7449
7450   GST_DEBUG_OBJECT (webrtc, "requesting aux receiver for session %u "
7451       "stream %" GST_PTR_FORMAT, session_id, stream);
7452
7453   ret = gst_bin_new (NULL);
7454
7455   stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL);
7456   gst_object_ref (stream->rtxreceive);
7457   if (!gst_bin_add (GST_BIN (ret), stream->rtxreceive))
7458     g_warn_if_reached ();
7459
7460   ensure_rtx_hdr_ext (stream);
7461
7462   stream->reddec = gst_element_factory_make ("rtpreddec", NULL);
7463   gst_object_ref (stream->reddec);
7464   if (!gst_bin_add (GST_BIN (ret), stream->reddec))
7465     g_warn_if_reached ();
7466
7467   _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7468
7469   if (!gst_element_link (stream->rtxreceive, stream->reddec))
7470     g_warn_if_reached ();
7471
7472   name = g_strdup_printf ("sink_%u", session_id);
7473   pad = gst_element_get_static_pad (stream->rtxreceive, "sink");
7474   ghost = gst_ghost_pad_new (name, pad);
7475   g_clear_pointer (&name, g_free);
7476   gst_clear_object (&pad);
7477   if (!gst_element_add_pad (ret, ghost))
7478     g_warn_if_reached ();
7479
7480   name = g_strdup_printf ("src_%u", session_id);
7481   pad = gst_element_get_static_pad (stream->reddec, "src");
7482   ghost = gst_ghost_pad_new (name, pad);
7483   g_clear_pointer (&name, g_free);
7484   gst_clear_object (&pad);
7485   if (!gst_element_add_pad (ret, ghost))
7486     g_warn_if_reached ();
7487
7488   return ret;
7489 }
7490
7491 static GstElement *
7492 on_rtpbin_request_fec_decoder_full (GstElement * rtpbin, guint session_id,
7493     guint ssrc, guint pt, GstWebRTCBin * webrtc)
7494 {
7495   TransportStream *stream;
7496   GstElement *ret = NULL;
7497   GObject *internal_storage;
7498
7499   stream = _find_transport_for_session (webrtc, session_id);
7500   if (!stream) {
7501     /* a rtp session without a stream is a webrtcbin bug */
7502     g_warn_if_reached ();
7503     return NULL;
7504   }
7505
7506   /* TODO: for now, we only support ulpfec, but once we support
7507    * more algorithms, if the remote may use more than one algorithm,
7508    * we will want to do the following:
7509    *
7510    * + Return a bin here, with the relevant FEC decoders plugged in
7511    *   and their payload type set to 0
7512    */
7513   GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u "
7514       "stream %" GST_PTR_FORMAT, pt, session_id, stream);
7515
7516   ret = gst_element_factory_make ("rtpulpfecdec", NULL);
7517
7518   g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
7519       &internal_storage);
7520
7521   g_object_set (ret, "storage", internal_storage, NULL);
7522   g_clear_object (&internal_storage);
7523
7524   g_object_set_data (G_OBJECT (ret), GST_WEBRTC_PAYLOAD_TYPE,
7525       GINT_TO_POINTER (pt));
7526
7527   PC_LOCK (webrtc);
7528   stream->fecdecs = g_list_prepend (stream->fecdecs, gst_object_ref (ret));
7529   _set_internal_rtpbin_element_props_from_stream (webrtc, stream);
7530   PC_UNLOCK (webrtc);
7531
7532   return ret;
7533 }
7534
7535 static void
7536 on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7537     GstWebRTCBin * webrtc)
7538 {
7539   GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc);
7540
7541   PC_LOCK (webrtc);
7542   remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7543   PC_UNLOCK (webrtc);
7544 }
7545
7546 static void
7547 on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7548     GstWebRTCBin * webrtc)
7549 {
7550   GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc);
7551
7552   PC_LOCK (webrtc);
7553   remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7554   PC_UNLOCK (webrtc);
7555 }
7556
7557 static void
7558 on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7559     GstWebRTCBin * webrtc)
7560 {
7561   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id,
7562       ssrc);
7563
7564   PC_LOCK (webrtc);
7565   remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7566   PC_UNLOCK (webrtc);
7567 }
7568
7569 static void
7570 on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7571     GstWebRTCBin * webrtc)
7572 {
7573   GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc);
7574
7575   if (ssrc == 0)
7576     return;
7577
7578   PC_LOCK (webrtc);
7579   find_or_add_ssrc_map_item (webrtc,
7580       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY, session_id, ssrc, -1);
7581   PC_UNLOCK (webrtc);
7582 }
7583
7584 static void
7585 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7586     GstWebRTCBin * webrtc)
7587 {
7588   GST_TRACE_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc);
7589 }
7590
7591 static void
7592 on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc,
7593     GstWebRTCBin * webrtc)
7594 {
7595   GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc);
7596 }
7597
7598 static void
7599 on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc,
7600     GstWebRTCBin * webrtc)
7601 {
7602   GObject *session;
7603
7604   GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc);
7605
7606   g_signal_emit_by_name (rtpbin, "get-internal-session", session_id, &session);
7607   if (session) {
7608     GObject *source;
7609
7610     g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7611     if (source) {
7612       GstStructure *sdes;
7613
7614       g_object_get (source, "sdes", &sdes, NULL);
7615
7616       /* TODO: when the sdes contains the mid, use that to correlate streams
7617        * as necessary */
7618       GST_DEBUG_OBJECT (webrtc, "session %u ssrc %u sdes %" GST_PTR_FORMAT,
7619           session_id, ssrc, sdes);
7620
7621       gst_clear_structure (&sdes);
7622       gst_clear_object (&source);
7623     }
7624     g_clear_object (&session);
7625   }
7626 }
7627
7628 static void
7629 on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc,
7630     GstWebRTCBin * webrtc)
7631 {
7632   GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc);
7633 }
7634
7635 static void
7636 on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc,
7637     GstWebRTCBin * webrtc)
7638 {
7639   GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc);
7640
7641   PC_LOCK (webrtc);
7642   remove_ssrc_entry_by_ssrc (webrtc, session_id, ssrc);
7643   PC_UNLOCK (webrtc);
7644 }
7645
7646 static void
7647 on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc,
7648     GstWebRTCBin * webrtc)
7649 {
7650   SsrcMapItem *mid;
7651
7652   GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id,
7653       ssrc);
7654
7655   PC_LOCK (webrtc);
7656   mid = find_mid_ssrc_for_ssrc (webrtc,
7657       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, session_id, ssrc);
7658   if (!mid) {
7659     TransportStream *stream = _find_transport_for_session (webrtc, session_id);
7660     transport_stream_add_ssrc_map_item (stream,
7661         GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, ssrc, -1);
7662   } else if (mid->mid) {
7663     /* XXX: when peers support the sdes rtcp item, use this to send the mid rtcp
7664      * sdes item.  Requires being able to set the sdes on the rtpsource. */
7665 #if 0
7666     GObject *session;
7667
7668     g_signal_emit_by_name (rtpbin, "get-internal-session", session_id,
7669         &session, NULL);
7670     if (session) {
7671       GObject *source;
7672
7673       g_signal_emit_by_name (session, "get-source-by-ssrc", ssrc, &source);
7674       if (source) {
7675         GstStructure *sdes;
7676         const char *sdes_field_name;
7677
7678         g_object_get (source, "sdes", &sdes, NULL);
7679         GST_WARNING_OBJECT (webrtc, "session %u ssrc %u retrieve sdes %"
7680             GST_PTR_FORMAT, session_id, ssrc, sdes);
7681         sdes_field_name = gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_MID);
7682         g_assert (sdes_field_name);
7683         gst_structure_set (sdes, sdes_field_name, G_TYPE_STRING, mid->mid,
7684             NULL);
7685         if (mid->rid) {
7686           sdes_field_name =
7687               gst_rtcp_sdes_type_to_name (GST_RTCP_SDES_RTP_STREAM_ID);
7688           g_assert (sdes_field_name);
7689           gst_structure_set (sdes, sdes_field_name, mid->rid, NULL);
7690           // TODO: repaired-rtp-stream-id
7691         }
7692         // TODO: writable sdes?
7693         g_object_set (source, "sdes", sdes, NULL);
7694         GST_INFO_OBJECT (webrtc,
7695             "session %u ssrc %u set sdes %" GST_PTR_FORMAT, session_id, ssrc,
7696             sdes);
7697
7698         gst_clear_structure (&sdes);
7699         gst_clear_object (&source);
7700       }
7701       g_clear_object (&session);
7702     }
7703 #endif
7704   }
7705   PC_UNLOCK (webrtc);
7706 }
7707
7708 static void
7709 on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
7710     GstWebRTCBin * webrtc)
7711 {
7712   GST_TRACE_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id,
7713       ssrc);
7714 }
7715
7716 struct new_jb_args
7717 {
7718   GstWebRTCBin *webrtc;
7719   GstElement *jitterbuffer;
7720   TransportStream *stream;
7721   guint ssrc;
7722 };
7723
7724 static gboolean
7725 jitter_buffer_set_retransmission (SsrcMapItem * item,
7726     const struct new_jb_args *data)
7727 {
7728   GstWebRTCRTPTransceiver *trans;
7729   gboolean do_nack;
7730
7731   if (item->media_idx == -1)
7732     return TRUE;
7733
7734   trans = _find_transceiver_for_mline (data->webrtc, item->media_idx);
7735   if (!trans) {
7736     g_warn_if_reached ();
7737     return TRUE;
7738   }
7739
7740   do_nack = WEBRTC_TRANSCEIVER (trans)->do_nack;
7741   /* We don't set do-retransmission on rtpbin as we want per-session control */
7742   GST_LOG_OBJECT (data->webrtc, "setting do-nack=%s for transceiver %"
7743       GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT
7744       " rtp session %u ssrc %u", do_nack ? "true" : "false", trans,
7745       data->stream, data->stream->session_id, data->ssrc);
7746   g_object_set (data->jitterbuffer, "do-retransmission", do_nack, NULL);
7747
7748   g_weak_ref_set (&item->rtpjitterbuffer, data->jitterbuffer);
7749
7750   return TRUE;
7751 }
7752
7753 static void
7754 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
7755     guint session_id, guint ssrc, GstWebRTCBin * webrtc)
7756 {
7757   TransportStream *stream;
7758   struct new_jb_args d = { 0, };
7759
7760   PC_LOCK (webrtc);
7761   GST_INFO_OBJECT (webrtc, "new jitterbuffer %" GST_PTR_FORMAT " for "
7762       "session %u ssrc %u", jitterbuffer, session_id, ssrc);
7763
7764   if (!(stream = _find_transport_for_session (webrtc, session_id))) {
7765     g_warn_if_reached ();
7766     goto out;
7767   }
7768
7769   d.webrtc = webrtc;
7770   d.jitterbuffer = jitterbuffer;
7771   d.stream = stream;
7772   d.ssrc = ssrc;
7773   transport_stream_filter_ssrc_map_item (stream, &d,
7774       (FindSsrcMapFunc) jitter_buffer_set_retransmission);
7775
7776 out:
7777   PC_UNLOCK (webrtc);
7778 }
7779
7780 static void
7781 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
7782     guint session_id, GstWebRTCBin * webrtc)
7783 {
7784   guint64 latency = webrtc->priv->jb_latency;
7785
7786   /* Add an extra 50 ms for safey */
7787   latency += RTPSTORAGE_EXTRA_TIME;
7788   latency *= GST_MSECOND;
7789
7790   g_object_set (storage, "size-time", latency, NULL);
7791 }
7792
7793 static GstElement *
7794 _create_rtpbin (GstWebRTCBin * webrtc)
7795 {
7796   GstElement *rtpbin;
7797
7798   if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
7799     return NULL;
7800
7801   /* mandated by WebRTC */
7802   gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
7803
7804   g_object_set (rtpbin, "do-lost", TRUE, NULL);
7805
7806   g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
7807       webrtc);
7808   g_signal_connect (rtpbin, "request-pt-map",
7809       G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
7810   g_signal_connect (rtpbin, "request-aux-sender",
7811       G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
7812   g_signal_connect (rtpbin, "request-aux-receiver",
7813       G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
7814   g_signal_connect (rtpbin, "new-storage",
7815       G_CALLBACK (on_rtpbin_new_storage), webrtc);
7816   g_signal_connect (rtpbin, "request-fec-decoder-full",
7817       G_CALLBACK (on_rtpbin_request_fec_decoder_full), webrtc);
7818   g_signal_connect (rtpbin, "on-bye-ssrc",
7819       G_CALLBACK (on_rtpbin_bye_ssrc), webrtc);
7820   g_signal_connect (rtpbin, "on-bye-timeout",
7821       G_CALLBACK (on_rtpbin_bye_timeout), webrtc);
7822   g_signal_connect (rtpbin, "on-new-ssrc",
7823       G_CALLBACK (on_rtpbin_new_ssrc), webrtc);
7824   g_signal_connect (rtpbin, "on-new-sender-ssrc",
7825       G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc);
7826   g_signal_connect (rtpbin, "on-sender-ssrc-active",
7827       G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc);
7828   g_signal_connect (rtpbin, "on-sender-timeout",
7829       G_CALLBACK (on_rtpbin_sender_timeout), webrtc);
7830   g_signal_connect (rtpbin, "on-ssrc-active",
7831       G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
7832   g_signal_connect (rtpbin, "on-ssrc-collision",
7833       G_CALLBACK (on_rtpbin_ssrc_collision), webrtc);
7834   g_signal_connect (rtpbin, "on-ssrc-sdes",
7835       G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc);
7836   g_signal_connect (rtpbin, "on-ssrc-validated",
7837       G_CALLBACK (on_rtpbin_ssrc_validated), webrtc);
7838   g_signal_connect (rtpbin, "on-timeout",
7839       G_CALLBACK (on_rtpbin_timeout), webrtc);
7840   g_signal_connect (rtpbin, "new-jitterbuffer",
7841       G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
7842
7843   return rtpbin;
7844 }
7845
7846 static GstStateChangeReturn
7847 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
7848 {
7849   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7850   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
7851
7852   GST_DEBUG ("changing state: %s => %s",
7853       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
7854       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
7855
7856   switch (transition) {
7857     case GST_STATE_CHANGE_NULL_TO_READY:{
7858       if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
7859         return GST_STATE_CHANGE_FAILURE;
7860       _start_thread (webrtc);
7861       PC_LOCK (webrtc);
7862       _update_need_negotiation (webrtc);
7863       PC_UNLOCK (webrtc);
7864       break;
7865     }
7866     case GST_STATE_CHANGE_READY_TO_PAUSED:
7867       webrtc->priv->running = TRUE;
7868       break;
7869     default:
7870       break;
7871   }
7872
7873   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
7874   if (ret == GST_STATE_CHANGE_FAILURE)
7875     return ret;
7876
7877   switch (transition) {
7878     case GST_STATE_CHANGE_READY_TO_PAUSED:
7879       /* Mangle the return value to NO_PREROLL as that's what really is
7880        * occurring here however cannot be propagated correctly due to nicesrc
7881        * requiring that it be in PLAYING already in order to send/receive
7882        * correctly :/ */
7883       ret = GST_STATE_CHANGE_NO_PREROLL;
7884       break;
7885     case GST_STATE_CHANGE_PAUSED_TO_READY:
7886       webrtc->priv->running = FALSE;
7887       break;
7888     case GST_STATE_CHANGE_READY_TO_NULL:
7889       _stop_thread (webrtc);
7890       break;
7891     default:
7892       break;
7893   }
7894
7895   return ret;
7896 }
7897
7898 static GstPadProbeReturn
7899 sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
7900 {
7901   GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
7902
7903   return GST_PAD_PROBE_OK;
7904 }
7905
7906 static void
7907 peek_sink_buffer (GstWebRTCBin * webrtc, guint rtp_session_id,
7908     guint media_idx, WebRTCTransceiver * trans, GstBuffer * buffer)
7909 {
7910   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
7911   SsrcMapItem *item;
7912   guint ssrc;
7913
7914   if (!gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp))
7915     return;
7916   ssrc = gst_rtp_buffer_get_ssrc (&rtp);
7917   gst_rtp_buffer_unmap (&rtp);
7918
7919   if (!ssrc) {
7920     GST_WARNING_OBJECT (webrtc,
7921         "incoming buffer does not contain a valid ssrc");
7922     return;
7923   }
7924
7925   PC_LOCK (webrtc);
7926   item =
7927       find_or_add_ssrc_map_item (webrtc,
7928       GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY, rtp_session_id, ssrc,
7929       media_idx);
7930   if (item->media_idx == -1) {
7931     char *str;
7932
7933     GST_DEBUG_OBJECT (webrtc, "updating media idx of ssrc item %p to %u", item,
7934         media_idx);
7935     item->media_idx = media_idx;
7936
7937     /* ensure that the rtx mapping contains a valid ssrc to use for rtx when
7938      * used even when there are no ssrc's in the input/codec preferences caps */
7939     str = g_strdup_printf ("%u", ssrc);
7940     if (!gst_structure_has_field_typed (trans->local_rtx_ssrc_map, str,
7941             G_TYPE_UINT)) {
7942       /* TODO: ssrc-collision? */
7943       gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
7944           g_random_int (), NULL);
7945       _set_internal_rtpbin_element_props_from_stream (webrtc, trans->stream);
7946     }
7947     g_free (str);
7948   }
7949   PC_UNLOCK (webrtc);
7950 }
7951
7952 static GstPadProbeReturn
7953 sink_pad_buffer_peek (GstPad * pad, GstPadProbeInfo * info,
7954     GstWebRTCBin * webrtc)
7955 {
7956   GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
7957   WebRTCTransceiver *trans;
7958   guint rtp_session_id, media_idx;
7959
7960   if (!webrtc_pad->trans)
7961     return GST_PAD_PROBE_OK;
7962
7963   trans = (WebRTCTransceiver *) webrtc_pad->trans;
7964   if (!trans->stream)
7965     return GST_PAD_PROBE_OK;
7966
7967   rtp_session_id = trans->stream->session_id;
7968   media_idx = webrtc_pad->trans->mline;
7969
7970   if (media_idx != G_MAXUINT)
7971     return GST_PAD_PROBE_OK;
7972
7973   if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
7974     GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
7975     peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
7976   } else if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
7977     GstBufferList *list = GST_PAD_PROBE_INFO_BUFFER_LIST (info);
7978     guint i, n;
7979
7980     n = gst_buffer_list_length (list);
7981     for (i = 0; i < n; i++) {
7982       GstBuffer *buffer = gst_buffer_list_get (list, i);
7983       peek_sink_buffer (webrtc, rtp_session_id, media_idx, trans, buffer);
7984     }
7985   } else {
7986     g_assert_not_reached ();
7987   }
7988
7989   return GST_PAD_PROBE_OK;
7990 }
7991
7992 static GstPad *
7993 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
7994     const gchar * name, const GstCaps * caps)
7995 {
7996   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
7997   GstWebRTCRTPTransceiver *trans = NULL;
7998   GstWebRTCBinPad *pad = NULL;
7999   guint serial;
8000   gboolean lock_mline = FALSE;
8001
8002   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
8003     return NULL;
8004
8005   if (templ->direction != GST_PAD_SINK ||
8006       g_strcmp0 (templ->name_template, "sink_%u") != 0) {
8007     GST_ERROR_OBJECT (element, "Requested pad that shouldn't be requestable");
8008     return NULL;
8009   }
8010
8011   PC_LOCK (webrtc);
8012
8013   if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
8014     /* no name given when requesting the pad, use next available int */
8015     serial = webrtc->priv->max_sink_pad_serial++;
8016   } else {
8017     /* parse serial number from requested padname */
8018     serial = g_ascii_strtoull (&name[5], NULL, 10);
8019     lock_mline = TRUE;
8020   }
8021
8022   if (lock_mline) {
8023     GstWebRTCBinPad *pad2;
8024
8025     trans = _find_transceiver_for_mline (webrtc, serial);
8026
8027     if (trans) {
8028       /* Reject transceivers that are only for receiving ... */
8029       if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
8030           trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) {
8031         GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8032             " existing m-line %d, but the transceiver's direction is %s",
8033             name, serial,
8034             gst_webrtc_rtp_transceiver_direction_to_string (trans->direction));
8035         goto error_out;
8036       }
8037
8038       /* Reject transceivers that already have a pad allocated */
8039       pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, trans);
8040       if (pad2) {
8041         GST_ERROR_OBJECT (element, "Trying to request pad %s for m-line %d, "
8042             " but the transceiver associated with this m-line already has pad"
8043             " %s", name, serial, GST_PAD_NAME (pad2));
8044         gst_object_unref (pad2);
8045         goto error_out;
8046       }
8047
8048       if (caps) {
8049         GST_OBJECT_LOCK (trans);
8050         if (trans->codec_preferences &&
8051             !gst_caps_can_intersect (caps, trans->codec_preferences)) {
8052           GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8053               " existing m-line %d, but requested caps %" GST_PTR_FORMAT
8054               " don't match existing codec preferences %" GST_PTR_FORMAT,
8055               name, serial, caps, trans->codec_preferences);
8056           GST_OBJECT_UNLOCK (trans);
8057           goto error_out;
8058         }
8059         GST_OBJECT_UNLOCK (trans);
8060
8061         if (trans->kind != GST_WEBRTC_KIND_UNKNOWN) {
8062           GstWebRTCKind kind = webrtc_kind_from_caps (caps);
8063
8064           if (trans->kind != kind) {
8065             GST_ERROR_OBJECT (element, "Tried to request a new sink pad %s for"
8066                 " existing m-line %d, but requested caps %" GST_PTR_FORMAT
8067                 " don't match transceiver kind %d",
8068                 name, serial, caps, trans->kind);
8069             goto error_out;
8070           }
8071         }
8072       }
8073     }
8074   }
8075
8076   /* Let's try to find a free transceiver that matches */
8077   if (!trans) {
8078     GstWebRTCKind kind = GST_WEBRTC_KIND_UNKNOWN;
8079     guint i;
8080
8081     kind = webrtc_kind_from_caps (caps);
8082
8083     for (i = 0; i < webrtc->priv->transceivers->len; i++) {
8084       GstWebRTCRTPTransceiver *tmptrans =
8085           g_ptr_array_index (webrtc->priv->transceivers, i);
8086       GstWebRTCBinPad *pad2;
8087       gboolean has_matching_caps;
8088
8089       /* Ignore transceivers with a non-matching kind */
8090       if (tmptrans->kind != GST_WEBRTC_KIND_UNKNOWN &&
8091           kind != GST_WEBRTC_KIND_UNKNOWN && tmptrans->kind != kind)
8092         continue;
8093
8094       /* Ignore stopped transmitters */
8095       if (tmptrans->stopped)
8096         continue;
8097
8098       /* Ignore transceivers that are only for receiving ... */
8099       if (tmptrans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY
8100           || tmptrans->direction ==
8101           GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
8102         continue;
8103
8104       /* Ignore transceivers that already have a pad allocated */
8105       pad2 = _find_pad_for_transceiver (webrtc, GST_PAD_SINK, tmptrans);
8106       if (pad2) {
8107         gst_object_unref (pad2);
8108         continue;
8109       }
8110
8111       GST_OBJECT_LOCK (tmptrans);
8112       has_matching_caps = (caps && tmptrans->codec_preferences &&
8113           !gst_caps_can_intersect (caps, tmptrans->codec_preferences));
8114       GST_OBJECT_UNLOCK (tmptrans);
8115       /* Ignore transceivers with non-matching caps */
8116       if (!has_matching_caps)
8117         continue;
8118
8119       trans = tmptrans;
8120       break;
8121     }
8122   }
8123
8124   if (!trans) {
8125     trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
8126             GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, -1,
8127             webrtc_kind_from_caps (caps), NULL));
8128     GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT, trans);
8129   } else {
8130     GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT
8131         " for mline %u", trans, serial);
8132     if (caps) {
8133       if (!_update_transceiver_kind_from_caps (trans, caps)) {
8134         GstWebRTCKind caps_kind = webrtc_kind_from_caps (caps);
8135
8136         GST_WARNING_OBJECT (webrtc,
8137             "Trying to change kind of transceiver %" GST_PTR_FORMAT
8138             " at m-line %d from %s (%d) to %s (%d)", trans, serial,
8139             gst_webrtc_kind_to_string (trans->kind), trans->kind,
8140             gst_webrtc_kind_to_string (caps_kind), caps_kind);
8141       }
8142     }
8143   }
8144   pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, trans, serial, NULL);
8145
8146   pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
8147       GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
8148       (GstPadProbeCallback) sink_pad_block, NULL, NULL);
8149   webrtc->priv->pending_sink_transceivers =
8150       g_list_append (webrtc->priv->pending_sink_transceivers,
8151       gst_object_ref (pad));
8152
8153   gst_pad_add_probe (GST_PAD (pad),
8154       GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
8155       (GstPadProbeCallback) sink_pad_buffer_peek, webrtc, NULL);
8156
8157   if (lock_mline) {
8158     WebRTCTransceiver *wtrans = WEBRTC_TRANSCEIVER (trans);
8159     wtrans->mline_locked = TRUE;
8160     trans->mline = serial;
8161   }
8162
8163   PC_UNLOCK (webrtc);
8164
8165   _add_pad (webrtc, pad);
8166
8167   return GST_PAD (pad);
8168
8169 error_out:
8170   PC_UNLOCK (webrtc);
8171   return NULL;
8172 }
8173
8174 static void
8175 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
8176 {
8177   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
8178   GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
8179
8180   GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad);
8181
8182   /* remove the transceiver from the pad so that subsequent code doesn't use
8183    * a possibly dead transceiver */
8184   PC_LOCK (webrtc);
8185   if (webrtc_pad->trans)
8186     gst_object_unref (webrtc_pad->trans);
8187   webrtc_pad->trans = NULL;
8188   gst_caps_replace (&webrtc_pad->received_caps, NULL);
8189   PC_UNLOCK (webrtc);
8190
8191   if (webrtc_pad->block_id) {
8192     gst_pad_remove_probe (GST_PAD (pad), webrtc_pad->block_id);
8193     webrtc_pad->block_id = 0;
8194   }
8195
8196   _remove_pad (webrtc, webrtc_pad);
8197
8198   PC_LOCK (webrtc);
8199   _update_need_negotiation (webrtc);
8200   PC_UNLOCK (webrtc);
8201 }
8202
8203 static void
8204 _update_rtpstorage_latency (GstWebRTCBin * webrtc)
8205 {
8206   guint i;
8207   guint64 latency_ns;
8208
8209   /* Add an extra 50 ms for safety */
8210   latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME;
8211   latency_ns *= GST_MSECOND;
8212
8213   for (i = 0; i < webrtc->priv->transports->len; i++) {
8214     TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
8215     GObject *storage = NULL;
8216
8217     g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id,
8218         &storage);
8219
8220     g_object_set (storage, "size-time", latency_ns, NULL);
8221
8222     g_object_unref (storage);
8223   }
8224 }
8225
8226 static void
8227 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
8228     const GValue * value, GParamSpec * pspec)
8229 {
8230   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8231
8232   switch (prop_id) {
8233     case PROP_STUN_SERVER:
8234       gst_webrtc_ice_set_stun_server (webrtc->priv->ice,
8235           g_value_get_string (value));
8236       break;
8237     case PROP_TURN_SERVER:
8238       gst_webrtc_ice_set_turn_server (webrtc->priv->ice,
8239           g_value_get_string (value));
8240       break;
8241     case PROP_BUNDLE_POLICY:
8242       if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
8243         GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
8244       } else {
8245         webrtc->bundle_policy = g_value_get_enum (value);
8246       }
8247       break;
8248     case PROP_ICE_TRANSPORT_POLICY:
8249       webrtc->ice_transport_policy = g_value_get_enum (value);
8250       gst_webrtc_ice_set_force_relay (webrtc->priv->ice,
8251           webrtc->ice_transport_policy ==
8252           GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE);
8253       break;
8254     case PROP_LATENCY:
8255       g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value);
8256       webrtc->priv->jb_latency = g_value_get_uint (value);
8257       _update_rtpstorage_latency (webrtc);
8258       break;
8259     case PROP_ICE_AGENT:
8260       webrtc->priv->ice = g_value_get_object (value);
8261       break;
8262     case PROP_HTTP_PROXY:
8263       gst_webrtc_ice_set_http_proxy (webrtc->priv->ice,
8264           g_value_get_string (value));
8265       break;
8266     default:
8267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8268       break;
8269   }
8270 }
8271
8272 static void
8273 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
8274     GValue * value, GParamSpec * pspec)
8275 {
8276   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8277
8278   PC_LOCK (webrtc);
8279   switch (prop_id) {
8280     case PROP_CONNECTION_STATE:
8281       g_value_set_enum (value, webrtc->peer_connection_state);
8282       break;
8283     case PROP_SIGNALING_STATE:
8284       g_value_set_enum (value, webrtc->signaling_state);
8285       break;
8286     case PROP_ICE_GATHERING_STATE:
8287       g_value_set_enum (value, webrtc->ice_gathering_state);
8288       break;
8289     case PROP_ICE_CONNECTION_STATE:
8290       g_value_set_enum (value, webrtc->ice_connection_state);
8291       break;
8292     case PROP_LOCAL_DESCRIPTION:
8293       if (webrtc->pending_local_description)
8294         g_value_set_boxed (value, webrtc->pending_local_description);
8295       else if (webrtc->current_local_description)
8296         g_value_set_boxed (value, webrtc->current_local_description);
8297       else
8298         g_value_set_boxed (value, NULL);
8299       break;
8300     case PROP_CURRENT_LOCAL_DESCRIPTION:
8301       g_value_set_boxed (value, webrtc->current_local_description);
8302       break;
8303     case PROP_PENDING_LOCAL_DESCRIPTION:
8304       g_value_set_boxed (value, webrtc->pending_local_description);
8305       break;
8306     case PROP_REMOTE_DESCRIPTION:
8307       if (webrtc->pending_remote_description)
8308         g_value_set_boxed (value, webrtc->pending_remote_description);
8309       else if (webrtc->current_remote_description)
8310         g_value_set_boxed (value, webrtc->current_remote_description);
8311       else
8312         g_value_set_boxed (value, NULL);
8313       break;
8314     case PROP_CURRENT_REMOTE_DESCRIPTION:
8315       g_value_set_boxed (value, webrtc->current_remote_description);
8316       break;
8317     case PROP_PENDING_REMOTE_DESCRIPTION:
8318       g_value_set_boxed (value, webrtc->pending_remote_description);
8319       break;
8320     case PROP_STUN_SERVER:
8321       g_value_take_string (value,
8322           gst_webrtc_ice_get_stun_server (webrtc->priv->ice));
8323       break;
8324     case PROP_TURN_SERVER:
8325       g_value_take_string (value,
8326           gst_webrtc_ice_get_turn_server (webrtc->priv->ice));
8327       break;
8328     case PROP_BUNDLE_POLICY:
8329       g_value_set_enum (value, webrtc->bundle_policy);
8330       break;
8331     case PROP_ICE_TRANSPORT_POLICY:
8332       g_value_set_enum (value, webrtc->ice_transport_policy);
8333       break;
8334     case PROP_ICE_AGENT:
8335       g_value_set_object (value, webrtc->priv->ice);
8336       break;
8337     case PROP_LATENCY:
8338       g_value_set_uint (value, webrtc->priv->jb_latency);
8339       break;
8340     case PROP_SCTP_TRANSPORT:
8341       g_value_set_object (value, webrtc->priv->sctp_transport);
8342       break;
8343     case PROP_HTTP_PROXY:
8344       g_value_take_string (value,
8345           gst_webrtc_ice_get_http_proxy (webrtc->priv->ice));
8346       break;
8347     default:
8348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
8349       break;
8350   }
8351   PC_UNLOCK (webrtc);
8352 }
8353
8354 static void
8355 gst_webrtc_bin_constructed (GObject * object)
8356 {
8357   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8358   gchar *name;
8359
8360   if (!webrtc->priv->ice) {
8361     name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc));
8362     webrtc->priv->ice = GST_WEBRTC_ICE (gst_webrtc_nice_new (name));
8363     g_free (name);
8364   }
8365   gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice,
8366       (GstWebRTCICEOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL);
8367
8368   G_OBJECT_CLASS (parent_class)->constructed (object);
8369 }
8370
8371 static void
8372 _free_pending_pad (GstPad * pad)
8373 {
8374   gst_object_unref (pad);
8375 }
8376
8377 static void
8378 gst_webrtc_bin_dispose (GObject * object)
8379 {
8380   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8381
8382   if (webrtc->priv->ice)
8383     gst_object_unref (webrtc->priv->ice);
8384   webrtc->priv->ice = NULL;
8385
8386   if (webrtc->priv->ice_stream_map)
8387     g_array_free (webrtc->priv->ice_stream_map, TRUE);
8388   webrtc->priv->ice_stream_map = NULL;
8389
8390   g_clear_object (&webrtc->priv->sctp_transport);
8391
8392   G_OBJECT_CLASS (parent_class)->dispose (object);
8393 }
8394
8395 static void
8396 gst_webrtc_bin_finalize (GObject * object)
8397 {
8398   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
8399
8400   if (webrtc->priv->transports)
8401     g_ptr_array_free (webrtc->priv->transports, TRUE);
8402   webrtc->priv->transports = NULL;
8403
8404   if (webrtc->priv->transceivers)
8405     g_ptr_array_free (webrtc->priv->transceivers, TRUE);
8406   webrtc->priv->transceivers = NULL;
8407
8408   if (webrtc->priv->data_channels)
8409     g_ptr_array_free (webrtc->priv->data_channels, TRUE);
8410   webrtc->priv->data_channels = NULL;
8411
8412   if (webrtc->priv->pending_data_channels)
8413     g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE);
8414   webrtc->priv->pending_data_channels = NULL;
8415
8416   if (webrtc->priv->pending_remote_ice_candidates)
8417     g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE);
8418   webrtc->priv->pending_remote_ice_candidates = NULL;
8419
8420   if (webrtc->priv->pending_local_ice_candidates)
8421     g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE);
8422   webrtc->priv->pending_local_ice_candidates = NULL;
8423
8424   if (webrtc->priv->pending_pads)
8425     g_list_free_full (webrtc->priv->pending_pads,
8426         (GDestroyNotify) _free_pending_pad);
8427   webrtc->priv->pending_pads = NULL;
8428
8429   if (webrtc->priv->pending_sink_transceivers)
8430     g_list_free_full (webrtc->priv->pending_sink_transceivers,
8431         (GDestroyNotify) gst_object_unref);
8432   webrtc->priv->pending_sink_transceivers = NULL;
8433
8434   if (webrtc->current_local_description)
8435     gst_webrtc_session_description_free (webrtc->current_local_description);
8436   webrtc->current_local_description = NULL;
8437   if (webrtc->pending_local_description)
8438     gst_webrtc_session_description_free (webrtc->pending_local_description);
8439   webrtc->pending_local_description = NULL;
8440
8441   if (webrtc->current_remote_description)
8442     gst_webrtc_session_description_free (webrtc->current_remote_description);
8443   webrtc->current_remote_description = NULL;
8444   if (webrtc->pending_remote_description)
8445     gst_webrtc_session_description_free (webrtc->pending_remote_description);
8446   webrtc->pending_remote_description = NULL;
8447
8448   if (webrtc->priv->last_generated_answer)
8449     gst_webrtc_session_description_free (webrtc->priv->last_generated_answer);
8450   webrtc->priv->last_generated_answer = NULL;
8451   if (webrtc->priv->last_generated_offer)
8452     gst_webrtc_session_description_free (webrtc->priv->last_generated_offer);
8453   webrtc->priv->last_generated_offer = NULL;
8454
8455   g_mutex_clear (DC_GET_LOCK (webrtc));
8456   g_mutex_clear (ICE_GET_LOCK (webrtc));
8457   g_mutex_clear (PC_GET_LOCK (webrtc));
8458   g_cond_clear (PC_GET_COND (webrtc));
8459
8460   G_OBJECT_CLASS (parent_class)->finalize (object);
8461 }
8462
8463 static void
8464 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
8465 {
8466   GObjectClass *gobject_class = (GObjectClass *) klass;
8467   GstElementClass *element_class = (GstElementClass *) klass;
8468
8469   element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
8470   element_class->release_pad = gst_webrtc_bin_release_pad;
8471   element_class->change_state = gst_webrtc_bin_change_state;
8472
8473   gst_element_class_add_static_pad_template_with_gtype (element_class,
8474       &sink_template, GST_TYPE_WEBRTC_BIN_SINK_PAD);
8475   gst_element_class_add_static_pad_template_with_gtype (element_class,
8476       &src_template, GST_TYPE_WEBRTC_BIN_SRC_PAD);
8477
8478   gst_element_class_set_metadata (element_class, "WebRTC Bin",
8479       "Filter/Network/WebRTC", "A bin for webrtc connections",
8480       "Matthew Waters <matthew@centricular.com>");
8481
8482   gobject_class->constructed = gst_webrtc_bin_constructed;
8483   gobject_class->get_property = gst_webrtc_bin_get_property;
8484   gobject_class->set_property = gst_webrtc_bin_set_property;
8485   gobject_class->dispose = gst_webrtc_bin_dispose;
8486   gobject_class->finalize = gst_webrtc_bin_finalize;
8487
8488   g_object_class_install_property (gobject_class,
8489       PROP_LOCAL_DESCRIPTION,
8490       g_param_spec_boxed ("local-description", "Local Description",
8491           "The local SDP description in use for this connection. "
8492           "Favours a pending description over the current description",
8493           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8494           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8495
8496   g_object_class_install_property (gobject_class,
8497       PROP_CURRENT_LOCAL_DESCRIPTION,
8498       g_param_spec_boxed ("current-local-description",
8499           "Current Local Description",
8500           "The local description that was successfully negotiated the last time "
8501           "the connection transitioned into the stable state",
8502           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8503           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8504
8505   g_object_class_install_property (gobject_class,
8506       PROP_PENDING_LOCAL_DESCRIPTION,
8507       g_param_spec_boxed ("pending-local-description",
8508           "Pending Local Description",
8509           "The local description that is in the process of being negotiated plus "
8510           "any local candidates that have been generated by the ICE Agent since the "
8511           "offer or answer was created",
8512           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8513           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8514
8515   g_object_class_install_property (gobject_class,
8516       PROP_REMOTE_DESCRIPTION,
8517       g_param_spec_boxed ("remote-description", "Remote Description",
8518           "The remote SDP description to use for this connection. "
8519           "Favours a pending description over the current description",
8520           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8521           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8522
8523   g_object_class_install_property (gobject_class,
8524       PROP_CURRENT_REMOTE_DESCRIPTION,
8525       g_param_spec_boxed ("current-remote-description",
8526           "Current Remote Description",
8527           "The last remote description that was successfully negotiated the last "
8528           "time the connection transitioned into the stable state plus any remote "
8529           "candidates that have been supplied via addIceCandidate() since the offer "
8530           "or answer was created",
8531           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8532           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8533
8534   g_object_class_install_property (gobject_class,
8535       PROP_PENDING_REMOTE_DESCRIPTION,
8536       g_param_spec_boxed ("pending-remote-description",
8537           "Pending Remote Description",
8538           "The remote description that is in the process of being negotiated, "
8539           "complete with any remote candidates that have been supplied via "
8540           "addIceCandidate() since the offer or answer was created",
8541           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
8542           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8543
8544   g_object_class_install_property (gobject_class,
8545       PROP_STUN_SERVER,
8546       g_param_spec_string ("stun-server", "STUN Server",
8547           "The STUN server of the form stun://hostname:port",
8548           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8549
8550   g_object_class_install_property (gobject_class,
8551       PROP_TURN_SERVER,
8552       g_param_spec_string ("turn-server", "TURN Server",
8553           "The TURN server of the form turn(s)://username:password@host:port. "
8554           "To use time-limited credentials, the form must be turn(s)://timestamp:"
8555           "username:password@host:port. Please note that the ':' character of "
8556           "the 'timestamp:username' and the 'password' encoded by base64 should "
8557           "be escaped to be parsed properly. "
8558           "This is a convenience property, use #GstWebRTCBin::add-turn-server "
8559           "if you wish to use multiple TURN servers",
8560           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8561
8562   g_object_class_install_property (gobject_class,
8563       PROP_CONNECTION_STATE,
8564       g_param_spec_enum ("connection-state", "Connection State",
8565           "The overall connection state of this element",
8566           GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
8567           GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
8568           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8569
8570   g_object_class_install_property (gobject_class,
8571       PROP_SIGNALING_STATE,
8572       g_param_spec_enum ("signaling-state", "Signaling State",
8573           "The signaling state of this element",
8574           GST_TYPE_WEBRTC_SIGNALING_STATE,
8575           GST_WEBRTC_SIGNALING_STATE_STABLE,
8576           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8577
8578   g_object_class_install_property (gobject_class,
8579       PROP_ICE_CONNECTION_STATE,
8580       g_param_spec_enum ("ice-connection-state", "ICE connection state",
8581           "The collective connection state of all ICETransport's",
8582           GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
8583           GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
8584           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8585
8586   g_object_class_install_property (gobject_class,
8587       PROP_ICE_GATHERING_STATE,
8588       g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
8589           "The collective gathering state of all ICETransport's",
8590           GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
8591           GST_WEBRTC_ICE_GATHERING_STATE_NEW,
8592           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8593
8594   g_object_class_install_property (gobject_class,
8595       PROP_BUNDLE_POLICY,
8596       g_param_spec_enum ("bundle-policy", "Bundle Policy",
8597           "The policy to apply for bundling",
8598           GST_TYPE_WEBRTC_BUNDLE_POLICY,
8599           GST_WEBRTC_BUNDLE_POLICY_NONE,
8600           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8601
8602   g_object_class_install_property (gobject_class,
8603       PROP_ICE_TRANSPORT_POLICY,
8604       g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
8605           "The policy to apply for ICE transport",
8606           GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
8607           GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
8608           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8609
8610   g_object_class_install_property (gobject_class,
8611       PROP_ICE_AGENT,
8612       g_param_spec_object ("ice-agent", "WebRTC ICE agent",
8613           "The WebRTC ICE agent",
8614           GST_TYPE_WEBRTC_ICE,
8615           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
8616
8617   /**
8618    * GstWebRTCBin:latency:
8619    *
8620    * Default duration to buffer in the jitterbuffers (in ms)
8621    *
8622    * Since: 1.18
8623    */
8624
8625   g_object_class_install_property (gobject_class,
8626       PROP_LATENCY,
8627       g_param_spec_uint ("latency", "Latency",
8628           "Default duration to buffer in the jitterbuffers (in ms)",
8629           0, G_MAXUINT, DEFAULT_JB_LATENCY,
8630           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8631
8632   /**
8633    * GstWebRTCBin:http-proxy:
8634    *
8635    * A HTTP proxy for use with TURN/TCP of the form
8636    * http://[username:password@]hostname[:port]
8637    *
8638    * Since: 1.22
8639    */
8640   g_object_class_install_property (gobject_class,
8641       PROP_HTTP_PROXY,
8642       g_param_spec_string ("http-proxy", "HTTP Proxy",
8643           "A HTTP proxy for use with TURN/TCP of the form "
8644           "http://[username:password@]hostname[:port]",
8645           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
8646
8647   /**
8648    * GstWebRTCBin:sctp-transport:
8649    *
8650    * The WebRTC SCTP Transport
8651    *
8652    * Since: 1.20
8653    */
8654   g_object_class_install_property (gobject_class,
8655       PROP_SCTP_TRANSPORT,
8656       g_param_spec_object ("sctp-transport", "WebRTC SCTP Transport",
8657           "The WebRTC SCTP Transport",
8658           GST_TYPE_WEBRTC_SCTP_TRANSPORT,
8659           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
8660
8661   /**
8662    * GstWebRTCBin::create-offer:
8663    * @object: the #webrtcbin
8664    * @options: (nullable): create-offer options
8665    * @promise: a #GstPromise which will contain the offer
8666    */
8667   gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
8668       g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
8669       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8670       G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL,
8671       G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8672
8673   /**
8674    * GstWebRTCBin::create-answer:
8675    * @object: the #webrtcbin
8676    * @options: (nullable): create-answer options
8677    * @promise: a #GstPromise which will contain the answer
8678    */
8679   gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
8680       g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
8681       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8682       G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL,
8683       G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE);
8684
8685   /**
8686    * GstWebRTCBin::set-local-description:
8687    * @object: the #GstWebRTCBin
8688    * @desc: a #GstWebRTCSessionDescription description
8689    * @promise: (nullable): a #GstPromise to be notified when it's set
8690    */
8691   gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
8692       g_signal_new_class_handler ("set-local-description",
8693       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8694       G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL,
8695       G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8696
8697   /**
8698    * GstWebRTCBin::set-remote-description:
8699    * @object: the #GstWebRTCBin
8700    * @desc: a #GstWebRTCSessionDescription description
8701    * @promise: (nullable): a #GstPromise to be notified when it's set
8702    */
8703   gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
8704       g_signal_new_class_handler ("set-remote-description",
8705       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8706       G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL,
8707       G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
8708
8709   /**
8710    * GstWebRTCBin::add-ice-candidate:
8711    * @object: the #webrtcbin
8712    * @mline_index: the index of the media description in the SDP
8713    * @ice-candidate: an ice candidate or NULL/"" to mark that no more candidates
8714    * will arrive
8715    */
8716   gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
8717       g_signal_new_class_handler ("add-ice-candidate",
8718       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8719       G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL,
8720       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8721
8722   /**
8723    * GstWebRTCBin::get-stats:
8724    * @object: the #webrtcbin
8725    * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
8726    * @promise: a #GstPromise for the result
8727    *
8728    * The @promise will contain the result of retrieving the session statistics.
8729    * The structure will be named 'application/x-webrtc-stats and contain the
8730    * following based on the webrtc-stats spec available from
8731    * https://www.w3.org/TR/webrtc-stats/.  As the webrtc-stats spec is a draft
8732    * and is constantly changing these statistics may be changed to fit with
8733    * the latest spec.
8734    *
8735    * Each field key is a unique identifier for each RTCStats
8736    * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
8737    * GstStructure) in the RTCStatsReport
8738    * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object).  Each supported
8739    * field in the RTCStats subclass is outlined below.
8740    *
8741    * Each statistics structure contains the following values as defined by
8742    * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
8743    *
8744    *  "timestamp"           G_TYPE_DOUBLE               timestamp the statistics were generated
8745    *  "type"                GST_TYPE_WEBRTC_STATS_TYPE  the type of statistics reported
8746    *  "id"                  G_TYPE_STRING               unique identifier
8747    *
8748    * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
8749    *
8750    *  "payload-type"        G_TYPE_UINT                 the rtp payload number in use
8751    *  "clock-rate"          G_TYPE_UINT                 the rtp clock-rate
8752    *
8753    * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
8754    *
8755    *  "ssrc"                G_TYPE_STRING               the rtp sequence src in use
8756    *  "transport-id"        G_TYPE_STRING               identifier for the associated RTCTransportStats for this stream
8757    *  "codec-id"            G_TYPE_STRING               identifier for the associated RTCCodecStats for this stream
8758    *  "kind"                G_TYPE_STRING               either "audio" or "video", depending on the associated transceiver (Since: 1.22)
8759    *
8760    * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
8761    *
8762    *  "packets-received"    G_TYPE_UINT64               number of packets received (only for local inbound)
8763    *  "packets-lost"        G_TYPE_INT64                number of packets lost
8764    *  "packets-discarded"   G_TYPE_UINT64               number of packets discarded
8765    *  "packets-repaired"    G_TYPE_UINT64               number of packets repaired
8766    *  "jitter"              G_TYPE_DOUBLE               packet jitter measured in seconds
8767    *
8768    * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
8769    *
8770    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteOutboundRTPStreamStats
8771    *  "bytes-received"      G_TYPE_UINT64               number of bytes received (only for local inbound)
8772    *  "packets-duplicated"  G_TYPE_UINT64               number of packets duplicated
8773    *  "fir-count"           G_TYPE_UINT                 FIR packets sent by the receiver
8774    *  "pli-count"           G_TYPE_UINT                 PLI packets sent by the receiver
8775    *  "nack-count"          G_TYPE_UINT                 NACK packets sent by the receiver
8776    *
8777    * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
8778    *
8779    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCOutboundRTPSTreamStats
8780    *  "round-trip-time"     G_TYPE_DOUBLE               round trip time of packets measured in seconds
8781    *  "fraction-lost"       G_TYPE_DOUBLE               fraction packet loss
8782    *
8783    * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
8784    *
8785    *  "packets-sent"        G_TYPE_UINT64               number of packets sent (only for local outbound)
8786    *  "bytes-sent"          G_TYPE_UINT64               number of packets sent (only for local outbound)
8787    *
8788    * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
8789    *
8790    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteInboundRTPSTreamStats (optional since 1.22)
8791    *  "fir-count"           G_TYPE_UINT                 FIR packets received by the sender
8792    *  "pli-count"           G_TYPE_UINT                 PLI packets received by the sender
8793    *  "nack-count"          G_TYPE_UINT                 NACK packets received by the sender
8794    *
8795    * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
8796    *
8797    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCInboundRTPSTreamStats
8798    *  "remote-timestamp"    G_TYPE_DOUBLE               remote timestamp the statistics were sent by the remote
8799    *
8800    * RTCIceCandidateStats supported fields (https://www.w3.org/TR/webrtc-stats/#icecandidate-dict*) (Since: 1.22)
8801    *
8802    *  "transport-id"         G_TYPE_STRING              identifier for the associated RTCTransportStats for this stream
8803    *  "address"              G_TYPE_STRING              address of the candidate, allowing for IPv4, IPv6 and FQDNs
8804    *  "port"                 G_TYPE_UINT                port number of the candidate
8805    *  "candidate-type"       G_TYPE_STRING              RTCIceCandidateType
8806    *  "priority"             G_TYPE_UINT64              calculated as defined in RFC 5245
8807    *  "protocol"             G_TYPE_STRING              Either "udp" or "tcp". Based on the "transport" defined in RFC 5245
8808    *  "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"
8809    *  "url"                  G_TYPE_STRING              URL of the ICE server from which the candidate was obtained. Only present for local candidates
8810    *
8811    * RTCIceCandidatePairStats supported fields (https://www.w3.org/TR/webrtc-stats/#candidatepair-dict*) (Since: 1.22)
8812    *
8813    *  "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.
8814    *  "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.
8815    */
8816   gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
8817       g_signal_new_class_handler ("get-stats",
8818       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8819       G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL,
8820       G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE);
8821
8822   /**
8823    * GstWebRTCBin::on-negotiation-needed:
8824    * @object: the #webrtcbin
8825    */
8826   gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
8827       g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
8828       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
8829
8830   /**
8831    * GstWebRTCBin::on-ice-candidate:
8832    * @object: the #webrtcbin
8833    * @mline_index: the index of the media description in the SDP
8834    * @candidate: the ICE candidate
8835    */
8836   gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
8837       g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
8838       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8839       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
8840
8841   /**
8842    * GstWebRTCBin::on-new-transceiver:
8843    * @object: the #webrtcbin
8844    * @candidate: the new #GstWebRTCRTPTransceiver
8845    */
8846   gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
8847       g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
8848       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8849       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
8850
8851   /**
8852    * GstWebRTCBin::on-data-channel:
8853    * @object: the #GstWebRTCBin
8854    * @channel: the new `GstWebRTCDataChannel`
8855    */
8856   gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
8857       g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
8858       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
8859       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
8860
8861   /**
8862    * GstWebRTCBin::prepare-data-channel:
8863    * @object: the #GstWebRTCBin
8864    * @channel: the new `GstWebRTCDataChannel`
8865    * @is_local: Whether this channel is local or remote
8866    *
8867    * Allows data-channel consumers to configure signal handlers on a newly
8868    * created data-channel, before any data or state change has been notified.
8869    *
8870    * Since: 1.22
8871    */
8872   gst_webrtc_bin_signals[PREPARE_DATA_CHANNEL_SIGNAL] =
8873       g_signal_new ("prepare-data-channel", G_TYPE_FROM_CLASS (klass),
8874       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2,
8875       GST_TYPE_WEBRTC_DATA_CHANNEL, G_TYPE_BOOLEAN);
8876
8877    /**
8878    * GstWebRTCBin::request-aux-sender:
8879    * @object: the #GstWebRTCBin
8880    * @dtls-transport: The #GstWebRTCDTLSTransport object for which the aux
8881    * sender will be used.
8882    *
8883    * Request an AUX sender element for the given @dtls-transport.
8884    *
8885    * Returns: (transfer full): A new GStreamer element
8886    *
8887    * Since: 1.22
8888    */
8889   gst_webrtc_bin_signals[REQUEST_AUX_SENDER] =
8890       g_signal_new ("request-aux-sender", G_TYPE_FROM_CLASS (klass),
8891       G_SIGNAL_RUN_LAST, 0, _gst_element_accumulator, NULL, NULL,
8892       GST_TYPE_ELEMENT, 1, GST_TYPE_WEBRTC_DTLS_TRANSPORT);
8893
8894   /**
8895    * GstWebRTCBin::add-transceiver:
8896    * @object: the #webrtcbin
8897    * @direction: the direction of the new transceiver
8898    * @caps: (allow none): the codec preferences for this transceiver
8899    *
8900    * Returns: the new #GstWebRTCRTPTransceiver
8901    */
8902   gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
8903       g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
8904       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8905       G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
8906       NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
8907       GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
8908
8909   /**
8910    * GstWebRTCBin::get-transceivers:
8911    * @object: the #webrtcbin
8912    *
8913    * Returns: a #GArray of #GstWebRTCRTPTransceivers
8914    */
8915   gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
8916       g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
8917       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8918       G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL,
8919       G_TYPE_ARRAY, 0);
8920
8921   /**
8922    * GstWebRTCBin::get-transceiver:
8923    * @object: the #GstWebRTCBin
8924    * @idx: The index of the transceiver
8925    *
8926    * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL
8927    * Since: 1.16
8928    */
8929   gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
8930       g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
8931       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8932       G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL,
8933       GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT);
8934
8935   /**
8936    * GstWebRTCBin::add-turn-server:
8937    * @object: the #GstWebRTCBin
8938    * @uri: The uri of the server of the form turn(s)://username:password@host:port
8939    *
8940    * Add a turn server to obtain ICE candidates from
8941    */
8942   gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
8943       g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
8944       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8945       G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL,
8946       G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
8947
8948   /**
8949    * GstWebRTCBin::create-data-channel:
8950    * @object: the #GstWebRTCBin
8951    * @label: the label for the data channel
8952    * @options: a #GstStructure of options for creating the data channel
8953    *
8954    * The options dictionary is the same format as the RTCDataChannelInit
8955    * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
8956    * and reproduced below
8957    *
8958    *  ordered               G_TYPE_BOOLEAN        Whether the channal will send data with guaranteed ordering
8959    *  max-packet-lifetime   G_TYPE_INT            The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
8960    *  max-retransmits       G_TYPE_INT            The number of times data will be attempted to be transmitted without acknowledgement before dropping
8961    *  protocol              G_TYPE_STRING         The subprotocol used by this channel
8962    *  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.
8963    *  id                    G_TYPE_INT            Override the default identifier selection of this channel
8964    *  priority              GST_TYPE_WEBRTC_PRIORITY_TYPE   The priority to use for this channel
8965    *
8966    * Returns: (transfer full): a new data channel object
8967    */
8968   gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
8969       g_signal_new_class_handler ("create-data-channel",
8970       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
8971       G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
8972       NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE);
8973
8974   gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0);
8975   gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SINK_PAD, 0);
8976   gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_SRC_PAD, 0);
8977 }
8978
8979 static void
8980 _unparent_and_unref (GObject * object)
8981 {
8982   GstObject *obj = GST_OBJECT (object);
8983
8984   GST_OBJECT_PARENT (obj) = NULL;
8985
8986   gst_object_unref (obj);
8987 }
8988
8989 static void
8990 _transport_free (GObject * object)
8991 {
8992   TransportStream *stream = (TransportStream *) object;
8993   GstWebRTCBin *webrtc;
8994
8995   webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
8996
8997   if (stream->transport) {
8998     g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
8999     g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
9000   }
9001
9002   gst_object_unref (object);
9003 }
9004
9005 static void
9006 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
9007 {
9008   /* Set SINK/SRC flags as webrtcbin can act as one depending on the
9009    * SDP later. Without setting this here already, surrounding bins might not
9010    * notice this and the pipeline configuration might become inconsistent,
9011    * e.g. with regards to latency.
9012    * See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/737
9013    */
9014   gst_bin_set_suppressed_flags (GST_BIN_CAST (webrtc),
9015       GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
9016   GST_OBJECT_FLAG_SET (webrtc, GST_ELEMENT_FLAG_SINK | GST_ELEMENT_FLAG_SOURCE);
9017
9018   webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
9019   g_mutex_init (PC_GET_LOCK (webrtc));
9020   g_cond_init (PC_GET_COND (webrtc));
9021
9022   g_mutex_init (ICE_GET_LOCK (webrtc));
9023   g_mutex_init (DC_GET_LOCK (webrtc));
9024
9025   webrtc->rtpbin = _create_rtpbin (webrtc);
9026   gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
9027
9028   webrtc->priv->transceivers =
9029       g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref);
9030   webrtc->priv->transports =
9031       g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free);
9032
9033   webrtc->priv->data_channels =
9034       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
9035
9036   webrtc->priv->pending_data_channels =
9037       g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref);
9038
9039   webrtc->priv->ice_stream_map =
9040       g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
9041   webrtc->priv->pending_remote_ice_candidates =
9042       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
9043   g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates,
9044       (GDestroyNotify) _clear_ice_candidate_item);
9045
9046   webrtc->priv->pending_local_ice_candidates =
9047       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem));
9048   g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates,
9049       (GDestroyNotify) _clear_ice_candidate_item);
9050
9051   /* we start off closed until we move to READY */
9052   webrtc->priv->is_closed = TRUE;
9053   webrtc->priv->jb_latency = DEFAULT_JB_LATENCY;
9054 }