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