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