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