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