webrtc: bail on invalid rtpbin names
[platform/upstream/gst-plugins-bad.git] / ext / webrtc / gstwebrtcbin.c
1 /* GStreamer
2  * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
28 #include "utils.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #define RANDOM_SESSION_ID \
37     ((((((guint64) g_random_int()) << 32) | \
38        (guint64) g_random_int ())) & \
39     G_GUINT64_CONSTANT (0x7fffffffffffffff))
40
41 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
42 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
43 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
44
45 #define PC_GET_COND(w) (&w->priv->pc_cond)
46 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
47 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
48 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
49
50 /*
51  * This webrtcbin implements the majority of the W3's peerconnection API and
52  * implementation guide where possible. Generating offers, answers and setting
53  * local and remote SDP's are all supported.  To start with, only the media
54  * interface has been implemented (no datachannel yet).
55  *
56  * Each input/output pad is equivalent to a Track in W3 parlance which are
57  * added/removed from the bin.  The number of requested sink pads is the number
58  * of streams that will be sent to the receiver and will be associated with a
59  * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
60  *
61  * On the receiving side, RTPTransceiver's are created in response to setting
62  * a remote description.  Output pads for the receiving streams in the set
63  * description are also created.
64  */
65
66 /*
67  * TODO:
68  * assert sending payload type matches the stream
69  * reconfiguration (of anything)
70  * LS groups
71  * bundling
72  * setting custom DTLS certificates
73  * data channel
74  *
75  * seperate session id's from mlineindex properly
76  * how to deal with replacing a input/output track/stream
77  */
78
79 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
80 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
81
82 GQuark
83 gst_webrtc_bin_error_quark (void)
84 {
85   return g_quark_from_static_string ("gst-webrtc-bin-error-quark");
86 }
87
88 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
89
90 static void
91 gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec)
93 {
94   switch (prop_id) {
95     default:
96       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
97       break;
98   }
99 }
100
101 static void
102 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
103     GValue * value, GParamSpec * pspec)
104 {
105   switch (prop_id) {
106     default:
107       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
108       break;
109   }
110 }
111
112 static void
113 gst_webrtc_bin_pad_finalize (GObject * object)
114 {
115   GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
116
117   if (pad->trans)
118     gst_object_unref (pad->trans);
119   pad->trans = NULL;
120
121   G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
122 }
123
124 static void
125 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
126 {
127   GObjectClass *gobject_class = (GObjectClass *) klass;
128
129   gobject_class->get_property = gst_webrtc_bin_pad_get_property;
130   gobject_class->set_property = gst_webrtc_bin_pad_set_property;
131   gobject_class->finalize = gst_webrtc_bin_pad_finalize;
132 }
133
134 static GstCaps *
135 _transport_stream_get_caps_for_pt (TransportStream * stream, guint pt)
136 {
137   guint i, len;
138
139   len = stream->ptmap->len;
140   for (i = 0; i < len; i++) {
141     PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
142     if (item->pt == pt)
143       return item->caps;
144   }
145   return NULL;
146 }
147
148 static void
149 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
150 {
151 }
152
153 static GstWebRTCBinPad *
154 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
155 {
156   GstWebRTCBinPad *pad =
157       g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
158       direction, NULL);
159
160   if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
161     gst_object_unref (pad);
162     return NULL;
163   }
164
165   GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
166       direction == GST_PAD_SRC ? "src" : "sink");
167   return pad;
168 }
169
170 #define gst_webrtc_bin_parent_class parent_class
171 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
172     GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
173         "webrtcbin element");
174     );
175
176 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
177     GST_PAD_SINK,
178     GST_PAD_REQUEST,
179     GST_STATIC_CAPS ("application/x-rtp"));
180
181 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
182     GST_PAD_SRC,
183     GST_PAD_SOMETIMES,
184     GST_STATIC_CAPS ("application/x-rtp"));
185
186 enum
187 {
188   SIGNAL_0,
189   CREATE_OFFER_SIGNAL,
190   CREATE_ANSWER_SIGNAL,
191   SET_LOCAL_DESCRIPTION_SIGNAL,
192   SET_REMOTE_DESCRIPTION_SIGNAL,
193   ADD_ICE_CANDIDATE_SIGNAL,
194   ON_NEGOTIATION_NEEDED_SIGNAL,
195   ON_ICE_CANDIDATE_SIGNAL,
196   GET_STATS_SIGNAL,
197   ADD_TRANSCEIVER_SIGNAL,
198   GET_TRANSCEIVERS_SIGNAL,
199   LAST_SIGNAL,
200 };
201
202 enum
203 {
204   PROP_0,
205   PROP_CONNECTION_STATE,
206   PROP_SIGNALING_STATE,
207   PROP_ICE_GATHERING_STATE,
208   PROP_ICE_CONNECTION_STATE,
209   PROP_LOCAL_DESCRIPTION,
210   PROP_CURRENT_LOCAL_DESCRIPTION,
211   PROP_PENDING_LOCAL_DESCRIPTION,
212   PROP_REMOTE_DESCRIPTION,
213   PROP_CURRENT_REMOTE_DESCRIPTION,
214   PROP_PENDING_REMOTE_DESCRIPTION,
215   PROP_STUN_SERVER,
216   PROP_TURN_SERVER,
217 };
218
219 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
220
221 static GstWebRTCDTLSTransport *
222 _transceiver_get_transport (GstWebRTCRTPTransceiver * trans)
223 {
224   if (trans->sender) {
225     return trans->sender->transport;
226   } else if (trans->receiver) {
227     return trans->receiver->transport;
228   }
229
230   return NULL;
231 }
232
233 static GstWebRTCDTLSTransport *
234 _transceiver_get_rtcp_transport (GstWebRTCRTPTransceiver * trans)
235 {
236   if (trans->sender) {
237     return trans->sender->rtcp_transport;
238   } else if (trans->receiver) {
239     return trans->receiver->rtcp_transport;
240   }
241
242   return NULL;
243 }
244
245 typedef struct
246 {
247   guint session_id;
248   GstWebRTCICEStream *stream;
249 } IceStreamItem;
250
251 /* FIXME: locking? */
252 GstWebRTCICEStream *
253 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
254 {
255   int i;
256
257   for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
258     IceStreamItem *item =
259         &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
260
261     if (item->session_id == session_id) {
262       GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
263           "session %u", item->stream, session_id);
264       return item->stream;
265     }
266   }
267
268   GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
269       session_id);
270   return NULL;
271 }
272
273 void
274 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
275     GstWebRTCICEStream * stream)
276 {
277   IceStreamItem item = { session_id, stream };
278
279   GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
280       "session %u", stream, session_id);
281   g_array_append_val (webrtc->priv->ice_stream_map, item);
282 }
283
284 typedef struct
285 {
286   guint session_id;
287   gchar *mid;
288 } SessionMidItem;
289
290 static void
291 clear_session_mid_item (SessionMidItem * item)
292 {
293   g_free (item->mid);
294 }
295
296 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
297     gconstpointer data);
298
299 static GstWebRTCRTPTransceiver *
300 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
301     FindTransceiverFunc func)
302 {
303   int i;
304
305   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
306     GstWebRTCRTPTransceiver *transceiver =
307         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
308         i);
309
310     if (func (transceiver, data))
311       return transceiver;
312   }
313
314   return NULL;
315 }
316
317 static gboolean
318 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
319 {
320   return g_strcmp0 (trans->mid, mid) == 0;
321 }
322
323 static gboolean
324 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
325 {
326   return trans->mline == *mline;
327 }
328
329 static GstWebRTCRTPTransceiver *
330 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
331 {
332   GstWebRTCRTPTransceiver *trans;
333
334   trans = _find_transceiver (webrtc, &mlineindex,
335       (FindTransceiverFunc) transceiver_match_for_mline);
336
337   GST_TRACE_OBJECT (webrtc,
338       "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
339       mlineindex);
340
341   return trans;
342 }
343
344 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
345     gconstpointer data);
346
347 static TransportStream *
348 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
349     FindTransportFunc func)
350 {
351   int i;
352
353   for (i = 0; i < webrtc->priv->transports->len; i++) {
354     TransportStream *stream =
355         g_array_index (webrtc->priv->transports, TransportStream *,
356         i);
357
358     if (func (stream, data))
359       return stream;
360   }
361
362   return NULL;
363 }
364
365 static gboolean
366 match_stream_for_session (TransportStream * trans, guint * session)
367 {
368   return trans->session_id == *session;
369 }
370
371 static TransportStream *
372 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
373 {
374   TransportStream *stream;
375
376   stream = _find_transport (webrtc, &session_id,
377       (FindTransportFunc) match_stream_for_session);
378
379   GST_TRACE_OBJECT (webrtc,
380       "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
381
382   return stream;
383 }
384
385 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
386
387 static GstWebRTCBinPad *
388 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
389 {
390   GstElement *element = GST_ELEMENT (webrtc);
391   GList *l;
392
393   GST_OBJECT_LOCK (webrtc);
394   l = element->pads;
395   for (; l; l = g_list_next (l)) {
396     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
397       continue;
398     if (func (l->data, data)) {
399       gst_object_ref (l->data);
400       GST_OBJECT_UNLOCK (webrtc);
401       return l->data;
402     }
403   }
404
405   l = webrtc->priv->pending_pads;
406   for (; l; l = g_list_next (l)) {
407     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
408       continue;
409     if (func (l->data, data)) {
410       gst_object_ref (l->data);
411       GST_OBJECT_UNLOCK (webrtc);
412       return l->data;
413     }
414   }
415   GST_OBJECT_UNLOCK (webrtc);
416
417   return NULL;
418 }
419
420 static void
421 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
422 {
423   GST_OBJECT_LOCK (webrtc);
424   webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
425   GST_OBJECT_UNLOCK (webrtc);
426 }
427
428 static void
429 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
430 {
431   GST_OBJECT_LOCK (webrtc);
432   webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
433   GST_OBJECT_UNLOCK (webrtc);
434 }
435
436 static void
437 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
438 {
439   _remove_pending_pad (webrtc, pad);
440
441   if (webrtc->priv->running)
442     gst_pad_set_active (GST_PAD (pad), TRUE);
443   gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
444 }
445
446 static void
447 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
448 {
449   _remove_pending_pad (webrtc, pad);
450
451   gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
452 }
453
454 typedef struct
455 {
456   GstPadDirection direction;
457   guint mlineindex;
458 } MLineMatch;
459
460 static gboolean
461 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
462 {
463   return GST_PAD_DIRECTION (pad) == match->direction
464       && pad->mlineindex == match->mlineindex;
465 }
466
467 static GstWebRTCBinPad *
468 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
469     guint mlineindex)
470 {
471   MLineMatch m = { direction, mlineindex };
472
473   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
474 }
475
476 typedef struct
477 {
478   GstPadDirection direction;
479   GstWebRTCRTPTransceiver *trans;
480 } TransMatch;
481
482 static gboolean
483 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
484 {
485   return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
486 }
487
488 static GstWebRTCBinPad *
489 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
490     GstWebRTCRTPTransceiver * trans)
491 {
492   TransMatch m = { direction, trans };
493
494   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
495 }
496
497 #if 0
498 static gboolean
499 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
500 {
501   return pad->ssrc == *ssrc;
502 }
503
504 static gboolean
505 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
506 {
507   return pad == other;
508 }
509 #endif
510
511 static gboolean
512 _unlock_pc_thread (GMutex * lock)
513 {
514   g_mutex_unlock (lock);
515   return G_SOURCE_REMOVE;
516 }
517
518 static gpointer
519 _gst_pc_thread (GstWebRTCBin * webrtc)
520 {
521   PC_LOCK (webrtc);
522   webrtc->priv->main_context = g_main_context_new ();
523   webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
524
525   PC_COND_BROADCAST (webrtc);
526   g_main_context_invoke (webrtc->priv->main_context,
527       (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
528
529   /* Having the thread be the thread default GMainContext will break the
530    * required queue-like ordering (from W3's peerconnection spec) of re-entrant
531    * tasks */
532   g_main_loop_run (webrtc->priv->loop);
533
534   PC_LOCK (webrtc);
535   g_main_context_unref (webrtc->priv->main_context);
536   webrtc->priv->main_context = NULL;
537   g_main_loop_unref (webrtc->priv->loop);
538   webrtc->priv->loop = NULL;
539   PC_COND_BROADCAST (webrtc);
540   PC_UNLOCK (webrtc);
541
542   return NULL;
543 }
544
545 static void
546 _start_thread (GstWebRTCBin * webrtc)
547 {
548   PC_LOCK (webrtc);
549   webrtc->priv->thread = g_thread_new ("gst-pc-ops",
550       (GThreadFunc) _gst_pc_thread, webrtc);
551
552   while (!webrtc->priv->loop)
553     PC_COND_WAIT (webrtc);
554   webrtc->priv->is_closed = FALSE;
555   PC_UNLOCK (webrtc);
556 }
557
558 static void
559 _stop_thread (GstWebRTCBin * webrtc)
560 {
561   PC_LOCK (webrtc);
562   webrtc->priv->is_closed = TRUE;
563   g_main_loop_quit (webrtc->priv->loop);
564   while (webrtc->priv->loop)
565     PC_COND_WAIT (webrtc);
566   PC_UNLOCK (webrtc);
567
568   g_thread_unref (webrtc->priv->thread);
569 }
570
571 static gboolean
572 _execute_op (GstWebRTCBinTask * op)
573 {
574   PC_LOCK (op->webrtc);
575   if (op->webrtc->priv->is_closed) {
576     GST_DEBUG_OBJECT (op->webrtc,
577         "Peerconnection is closed, aborting execution");
578     goto out;
579   }
580
581   op->op (op->webrtc, op->data);
582
583 out:
584   PC_UNLOCK (op->webrtc);
585   return G_SOURCE_REMOVE;
586 }
587
588 static void
589 _free_op (GstWebRTCBinTask * op)
590 {
591   if (op->notify)
592     op->notify (op->data);
593   g_free (op);
594 }
595
596 void
597 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
598     gpointer data, GDestroyNotify notify)
599 {
600   GstWebRTCBinTask *op;
601   GSource *source;
602
603   g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc));
604
605   if (webrtc->priv->is_closed) {
606     GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
607     if (notify)
608       notify (data);
609     return;
610   }
611   op = g_new0 (GstWebRTCBinTask, 1);
612   op->webrtc = webrtc;
613   op->op = func;
614   op->data = data;
615   op->notify = notify;
616
617   source = g_idle_source_new ();
618   g_source_set_priority (source, G_PRIORITY_DEFAULT);
619   g_source_set_callback (source, (GSourceFunc) _execute_op, op,
620       (GDestroyNotify) _free_op);
621   g_source_attach (source, webrtc->priv->main_context);
622   g_source_unref (source);
623 }
624
625 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
626 static GstWebRTCICEConnectionState
627 _collate_ice_connection_states (GstWebRTCBin * webrtc)
628 {
629 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
630   GstWebRTCICEConnectionState any_state = 0;
631   gboolean all_closed = TRUE;
632   int i;
633
634   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
635     GstWebRTCRTPTransceiver *rtp_trans =
636         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
637         i);
638     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
639     TransportStream *stream = trans->stream;
640     GstWebRTCICETransport *transport, *rtcp_transport;
641     GstWebRTCICEConnectionState ice_state;
642     gboolean rtcp_mux = FALSE;
643
644     if (rtp_trans->stopped)
645       continue;
646     if (!rtp_trans->mid)
647       continue;
648
649     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
650
651     transport = _transceiver_get_transport (rtp_trans)->transport;
652
653     /* get transport state */
654     g_object_get (transport, "state", &ice_state, NULL);
655     any_state |= (1 << ice_state);
656     if (ice_state != STATE (CLOSED))
657       all_closed = FALSE;
658
659     rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans)->transport;
660
661     if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) {
662       g_object_get (rtcp_transport, "state", &ice_state, NULL);
663       any_state |= (1 << ice_state);
664       if (ice_state != STATE (CLOSED))
665         all_closed = FALSE;
666     }
667   }
668
669   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
670
671   if (webrtc->priv->is_closed) {
672     GST_TRACE_OBJECT (webrtc, "returning closed");
673     return STATE (CLOSED);
674   }
675   /* Any of the RTCIceTransport s are in the failed state. */
676   if (any_state & (1 << STATE (FAILED))) {
677     GST_TRACE_OBJECT (webrtc, "returning failed");
678     return STATE (FAILED);
679   }
680   /* Any of the RTCIceTransport s are in the disconnected state and
681    * none of them are in the failed state. */
682   if (any_state & (1 << STATE (DISCONNECTED))) {
683     GST_TRACE_OBJECT (webrtc, "returning disconnected");
684     return STATE (DISCONNECTED);
685   }
686   /* Any of the RTCIceTransport's are in the checking state and none of them
687    * are in the failed or disconnected state. */
688   if (any_state & (1 << STATE (CHECKING))) {
689     GST_TRACE_OBJECT (webrtc, "returning checking");
690     return STATE (CHECKING);
691   }
692   /* Any of the RTCIceTransport s are in the new state and none of them are
693    * in the checking, failed or disconnected state, or all RTCIceTransport's
694    * are in the closed state. */
695   if ((any_state & (1 << STATE (NEW))) || all_closed) {
696     GST_TRACE_OBJECT (webrtc, "returning new");
697     return STATE (NEW);
698   }
699   /* All RTCIceTransport s are in the connected, completed or closed state
700    * and at least one of them is in the connected state. */
701   if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 <<
702           STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) {
703     GST_TRACE_OBJECT (webrtc, "returning connected");
704     return STATE (CONNECTED);
705   }
706   /* All RTCIceTransport s are in the completed or closed state and at least
707    * one of them is in the completed state. */
708   if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED))
709       && any_state & (1 << STATE (COMPLETED))) {
710     GST_TRACE_OBJECT (webrtc, "returning connected");
711     return STATE (CONNECTED);
712   }
713
714   GST_FIXME ("unspecified situation, returning new");
715   return STATE (NEW);
716 #undef STATE
717 }
718
719 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
720 static GstWebRTCICEGatheringState
721 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
722 {
723 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
724   GstWebRTCICEGatheringState any_state = 0;
725   gboolean all_completed = webrtc->priv->transceivers->len > 0;
726   int i;
727
728   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
729     GstWebRTCRTPTransceiver *rtp_trans =
730         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
731         i);
732     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
733     TransportStream *stream = trans->stream;
734     GstWebRTCICETransport *transport, *rtcp_transport;
735     GstWebRTCICEGatheringState ice_state;
736     gboolean rtcp_mux = FALSE;
737
738     if (rtp_trans->stopped)
739       continue;
740     if (!rtp_trans->mid)
741       continue;
742
743     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
744
745     transport = _transceiver_get_transport (rtp_trans)->transport;
746
747     /* get gathering state */
748     g_object_get (transport, "gathering-state", &ice_state, NULL);
749     any_state |= (1 << ice_state);
750     if (ice_state != STATE (COMPLETE))
751       all_completed = FALSE;
752
753     rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans)->transport;
754
755     if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
756       g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL);
757       any_state |= (1 << ice_state);
758       if (ice_state != STATE (COMPLETE))
759         all_completed = FALSE;
760     }
761   }
762
763   GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
764
765   /* Any of the RTCIceTransport s are in the gathering state. */
766   if (any_state & (1 << STATE (GATHERING))) {
767     GST_TRACE_OBJECT (webrtc, "returning gathering");
768     return STATE (GATHERING);
769   }
770   /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
771    * the completed gathering state. */
772   if (all_completed) {
773     GST_TRACE_OBJECT (webrtc, "returning complete");
774     return STATE (COMPLETE);
775   }
776
777   /* Any of the RTCIceTransport s are in the new gathering state and none
778    * of the transports are in the gathering state, or there are no transports. */
779   GST_TRACE_OBJECT (webrtc, "returning new");
780   return STATE (NEW);
781 #undef STATE
782 }
783
784 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
785 static GstWebRTCPeerConnectionState
786 _collate_peer_connection_states (GstWebRTCBin * webrtc)
787 {
788 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
789 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
790 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
791   GstWebRTCICEConnectionState any_ice_state = 0;
792   GstWebRTCDTLSTransportState any_dtls_state = 0;
793   int i;
794
795   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
796     GstWebRTCRTPTransceiver *rtp_trans =
797         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
798         i);
799     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
800     TransportStream *stream = trans->stream;
801     GstWebRTCDTLSTransport *transport, *rtcp_transport;
802     GstWebRTCICEGatheringState ice_state;
803     GstWebRTCDTLSTransportState dtls_state;
804     gboolean rtcp_mux = FALSE;
805
806     if (rtp_trans->stopped)
807       continue;
808     if (!rtp_trans->mid)
809       continue;
810
811     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
812     transport = _transceiver_get_transport (rtp_trans);
813
814     /* get transport state */
815     g_object_get (transport, "state", &dtls_state, NULL);
816     any_dtls_state |= (1 << dtls_state);
817     g_object_get (transport->transport, "state", &ice_state, NULL);
818     any_ice_state |= (1 << ice_state);
819
820     rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans);
821
822     if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
823       g_object_get (rtcp_transport, "state", &dtls_state, NULL);
824       any_dtls_state |= (1 << dtls_state);
825       g_object_get (rtcp_transport->transport, "state", &ice_state, NULL);
826       any_ice_state |= (1 << ice_state);
827     }
828   }
829
830   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
831       "state: 0x%x", any_ice_state, any_dtls_state);
832
833   /* The RTCPeerConnection object's [[ isClosed]] slot is true.  */
834   if (webrtc->priv->is_closed) {
835     GST_TRACE_OBJECT (webrtc, "returning closed");
836     return STATE (CLOSED);
837   }
838
839   /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
840   if (any_ice_state & (1 << ICE_STATE (FAILED))) {
841     GST_TRACE_OBJECT (webrtc, "returning failed");
842     return STATE (FAILED);
843   }
844   if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
845     GST_TRACE_OBJECT (webrtc, "returning failed");
846     return STATE (FAILED);
847   }
848
849   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting
850    * or checking state and none of them is in the failed state. */
851   if (any_ice_state & (1 << ICE_STATE (CHECKING))) {
852     GST_TRACE_OBJECT (webrtc, "returning connecting");
853     return STATE (CONNECTING);
854   }
855   if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) {
856     GST_TRACE_OBJECT (webrtc, "returning connecting");
857     return STATE (CONNECTING);
858   }
859
860   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
861    * state and none of them are in the failed or connecting or checking state. */
862   if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
863     GST_TRACE_OBJECT (webrtc, "returning disconnected");
864     return STATE (DISCONNECTED);
865   }
866
867   /* All RTCIceTransport's and RTCDtlsTransport's are in the connected,
868    * completed or closed state and at least of them is in the connected or
869    * completed state. */
870   if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 <<
871               ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED)))
872       && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 <<
873               DTLS_STATE (CLOSED)))
874       && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 <<
875               ICE_STATE (COMPLETED))
876           || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) {
877     GST_TRACE_OBJECT (webrtc, "returning connected");
878     return STATE (CONNECTED);
879   }
880
881   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state
882    * and none of the transports are in the connecting, checking, failed or
883    * disconnected state, or all transports are in the closed state. */
884   if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) {
885     GST_TRACE_OBJECT (webrtc, "returning new");
886     return STATE (NEW);
887   }
888   if ((any_ice_state & (1 << ICE_STATE (NEW))
889           || any_dtls_state & (1 << DTLS_STATE (NEW)))
890       && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED)
891               | (1 << ICE_STATE (DISCONNECTED))))
892       && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 <<
893               DTLS_STATE (FAILED)))) {
894     GST_TRACE_OBJECT (webrtc, "returning new");
895     return STATE (NEW);
896   }
897
898   GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new");
899   return STATE (NEW);
900 #undef DTLS_STATE
901 #undef ICE_STATE
902 #undef STATE
903 }
904
905 static void
906 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
907 {
908   GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
909   GstWebRTCICEGatheringState new_state;
910
911   new_state = _collate_ice_gathering_states (webrtc);
912
913   if (new_state != webrtc->ice_gathering_state) {
914     gchar *old_s, *new_s;
915
916     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
917         old_state);
918     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
919         new_state);
920     GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
921         old_s, old_state, new_s, new_state);
922     g_free (old_s);
923     g_free (new_s);
924
925     webrtc->ice_gathering_state = new_state;
926     PC_UNLOCK (webrtc);
927     g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
928     PC_LOCK (webrtc);
929   }
930 }
931
932 static void
933 _update_ice_gathering_state (GstWebRTCBin * webrtc)
934 {
935   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
936       NULL);
937 }
938
939 static void
940 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
941 {
942   GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
943   GstWebRTCICEConnectionState new_state;
944
945   new_state = _collate_ice_connection_states (webrtc);
946
947   if (new_state != old_state) {
948     gchar *old_s, *new_s;
949
950     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
951         old_state);
952     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
953         new_state);
954     GST_INFO_OBJECT (webrtc,
955         "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
956         new_s, new_state);
957     g_free (old_s);
958     g_free (new_s);
959
960     webrtc->ice_connection_state = new_state;
961     PC_UNLOCK (webrtc);
962     g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
963     PC_LOCK (webrtc);
964   }
965 }
966
967 static void
968 _update_ice_connection_state (GstWebRTCBin * webrtc)
969 {
970   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
971       NULL);
972 }
973
974 static void
975 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
976 {
977   GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
978   GstWebRTCPeerConnectionState new_state;
979
980   new_state = _collate_peer_connection_states (webrtc);
981
982   if (new_state != old_state) {
983     gchar *old_s, *new_s;
984
985     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
986         old_state);
987     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
988         new_state);
989     GST_INFO_OBJECT (webrtc,
990         "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
991         new_s, new_state);
992     g_free (old_s);
993     g_free (new_s);
994
995     webrtc->peer_connection_state = new_state;
996     PC_UNLOCK (webrtc);
997     g_object_notify (G_OBJECT (webrtc), "connection-state");
998     PC_LOCK (webrtc);
999   }
1000 }
1001
1002 static void
1003 _update_peer_connection_state (GstWebRTCBin * webrtc)
1004 {
1005   gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1006       NULL, NULL);
1007 }
1008
1009 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1010 static gboolean
1011 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1012 {
1013   int i;
1014
1015   GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1016
1017   /* If any implementation-specific negotiation is required, as described at
1018    * the start of this section, return "true".
1019    * FIXME */
1020   /* FIXME: emit when input caps/format changes? */
1021
1022   /* If connection has created any RTCDataChannel's, and no m= section has
1023    * been negotiated yet for data, return "true". 
1024    * FIXME */
1025
1026   if (!webrtc->current_local_description) {
1027     GST_LOG_OBJECT (webrtc, "no local description set");
1028     return TRUE;
1029   }
1030
1031   if (!webrtc->current_remote_description) {
1032     GST_LOG_OBJECT (webrtc, "no remote description set");
1033     return TRUE;
1034   }
1035
1036   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1037     GstWebRTCRTPTransceiver *trans;
1038
1039     trans =
1040         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1041         i);
1042
1043     if (trans->stopped) {
1044       /* FIXME: If t is stopped and is associated with an m= section according to
1045        * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1046        * rejected in connection's currentLocalDescription or
1047        * currentRemoteDescription , return "true". */
1048       GST_FIXME_OBJECT (webrtc,
1049           "check if the transceiver is rejected in descriptions");
1050     } else {
1051       const GstSDPMedia *media;
1052       GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1053
1054       if (trans->mline == -1) {
1055         GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT,
1056             i, trans);
1057         return TRUE;
1058       }
1059       /* internal inconsistency */
1060       g_assert (trans->mline <
1061           gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1062       g_assert (trans->mline <
1063           gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1064
1065       /* FIXME: msid handling
1066        * If t's direction is "sendrecv" or "sendonly", and the associated m=
1067        * section in connection's currentLocalDescription doesn't contain an
1068        * "a=msid" line, return "true". */
1069
1070       media =
1071           gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1072           trans->mline);
1073       local_dir = _get_direction_from_media (media);
1074
1075       media =
1076           gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1077           trans->mline);
1078       remote_dir = _get_direction_from_media (media);
1079
1080       if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1081         /* If connection's currentLocalDescription if of type "offer", and
1082          * the direction of the associated m= section in neither the offer
1083          * nor answer matches t's direction, return "true". */
1084
1085         if (local_dir != trans->direction && remote_dir != trans->direction) {
1086           GST_LOG_OBJECT (webrtc,
1087               "transceiver direction doesn't match description");
1088           return TRUE;
1089         }
1090       } else if (webrtc->current_local_description->type ==
1091           GST_WEBRTC_SDP_TYPE_ANSWER) {
1092         GstWebRTCRTPTransceiverDirection intersect_dir;
1093
1094         /* If connection's currentLocalDescription if of type "answer", and
1095          * the direction of the associated m= section in the answer does not
1096          * match t's direction intersected with the offered direction (as
1097          * described in [JSEP] (section 5.3.1.)), return "true". */
1098
1099         /* remote is the offer, local is the answer */
1100         intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1101
1102         if (intersect_dir != trans->direction) {
1103           GST_LOG_OBJECT (webrtc,
1104               "transceiver direction doesn't match description");
1105           return TRUE;
1106         }
1107       }
1108     }
1109   }
1110
1111   GST_LOG_OBJECT (webrtc, "no negotiation needed");
1112   return FALSE;
1113 }
1114
1115 static void
1116 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1117 {
1118   if (webrtc->priv->need_negotiation) {
1119     GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1120     PC_UNLOCK (webrtc);
1121     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1122         0);
1123     PC_LOCK (webrtc);
1124   }
1125 }
1126
1127 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1128 static void
1129 _update_need_negotiation (GstWebRTCBin * webrtc)
1130 {
1131   /* If connection's [[isClosed]] slot is true, abort these steps. */
1132   if (webrtc->priv->is_closed)
1133     return;
1134   /* If connection's signaling state is not "stable", abort these steps. */
1135   if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1136     return;
1137
1138   /* If the result of checking if negotiation is needed is "false", clear the
1139    * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1140    * to false, and abort these steps. */
1141   if (!_check_if_negotiation_is_needed (webrtc)) {
1142     webrtc->priv->need_negotiation = FALSE;
1143     return;
1144   }
1145   /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1146   if (webrtc->priv->need_negotiation)
1147     return;
1148   /* Set connection's [[needNegotiation]] slot to true. */
1149   webrtc->priv->need_negotiation = TRUE;
1150   /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1151    * true, fire a simple event named negotiationneeded at connection. */
1152   gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1153       NULL);
1154 }
1155
1156 static GstCaps *
1157 _find_codec_preferences (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * trans,
1158     GstPadDirection direction, guint media_idx)
1159 {
1160   GstCaps *ret = NULL;
1161
1162   GST_LOG_OBJECT (webrtc, "retreiving codec preferences from %" GST_PTR_FORMAT,
1163       trans);
1164
1165   if (trans->codec_preferences) {
1166     GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1167         trans->codec_preferences);
1168     ret = gst_caps_ref (trans->codec_preferences);
1169   } else {
1170     GstWebRTCBinPad *pad = _find_pad_for_mline (webrtc, direction, media_idx);
1171     if (pad) {
1172       GstCaps *caps = gst_pad_get_current_caps (GST_PAD (pad));
1173       if (caps) {
1174         GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1175             caps);
1176       } else {
1177         if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
1178           GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1179               caps);
1180       }
1181       if (caps)
1182         ret = caps;
1183       gst_object_unref (pad);
1184     }
1185   }
1186
1187   return ret;
1188 }
1189
1190 static GstCaps *
1191 _add_supported_attributes_to_caps (const GstCaps * caps)
1192 {
1193   GstCaps *ret;
1194   int i;
1195
1196   ret = gst_caps_make_writable (caps);
1197
1198   for (i = 0; i < gst_caps_get_size (ret); i++) {
1199     GstStructure *s = gst_caps_get_structure (ret, i);
1200
1201     if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1202       gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1203     if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1204       gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1205     /* FIXME: is this needed? */
1206     /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1207        gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1208
1209     /* FIXME: codec-specific paramters? */
1210   }
1211
1212   return ret;
1213 }
1214
1215 static void
1216 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1217     GParamSpec * pspec, GstWebRTCBin * webrtc)
1218 {
1219   _update_ice_connection_state (webrtc);
1220   _update_peer_connection_state (webrtc);
1221 }
1222
1223 static void
1224 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1225     GParamSpec * pspec, GstWebRTCBin * webrtc)
1226 {
1227   _update_ice_gathering_state (webrtc);
1228 }
1229
1230 static void
1231 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1232     GParamSpec * pspec, GstWebRTCBin * webrtc)
1233 {
1234   _update_peer_connection_state (webrtc);
1235 }
1236
1237 static WebRTCTransceiver *
1238 _create_webrtc_transceiver (GstWebRTCBin * webrtc)
1239 {
1240   WebRTCTransceiver *trans;
1241   GstWebRTCRTPTransceiver *rtp_trans;
1242   GstWebRTCRTPSender *sender;
1243   GstWebRTCRTPReceiver *receiver;
1244
1245   sender = gst_webrtc_rtp_sender_new (NULL);
1246   receiver = gst_webrtc_rtp_receiver_new ();
1247   trans = webrtc_transceiver_new (webrtc, sender, receiver);
1248   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1249   rtp_trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
1250   rtp_trans->mline = -1;
1251
1252   g_array_append_val (webrtc->priv->transceivers, trans);
1253
1254   gst_object_unref (sender);
1255   gst_object_unref (receiver);
1256
1257   return trans;
1258 }
1259
1260 static TransportStream *
1261 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1262 {
1263   GstWebRTCDTLSTransport *transport;
1264   TransportStream *ret;
1265   gchar *pad_name;
1266
1267   /* FIXME: how to parametrize the sender and the receiver */
1268   ret = transport_stream_new (webrtc, session_id);
1269   transport = ret->transport;
1270
1271   g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1272       G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1273   g_signal_connect (G_OBJECT (transport->transport),
1274       "notify::gathering-state",
1275       G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1276   g_signal_connect (G_OBJECT (transport), "notify::state",
1277       G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1278
1279   if ((transport = ret->rtcp_transport)) {
1280     g_signal_connect (G_OBJECT (transport->transport),
1281         "notify::state", G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1282     g_signal_connect (G_OBJECT (transport->transport),
1283         "notify::gathering-state",
1284         G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1285     g_signal_connect (G_OBJECT (transport), "notify::state",
1286         G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1287   }
1288
1289   gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1290   gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1291
1292   pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1293   if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1294           GST_ELEMENT (webrtc->rtpbin), pad_name))
1295     g_warn_if_reached ();
1296   g_free (pad_name);
1297
1298   pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1299   if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1300           GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1301     g_warn_if_reached ();
1302   g_free (pad_name);
1303
1304   g_array_append_val (webrtc->priv->transports, ret);
1305
1306   GST_TRACE_OBJECT (webrtc,
1307       "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1308
1309   gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1310   gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1311
1312   return ret;
1313 }
1314
1315 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
1316 static gboolean
1317 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
1318     GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx)
1319 {
1320   /* TODO:
1321    * rtp header extensions
1322    * ice attributes
1323    * rtx
1324    * fec
1325    * msid-semantics
1326    * msid
1327    * dtls fingerprints
1328    * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
1329    */
1330   gchar *direction, *sdp_mid;
1331   GstCaps *caps;
1332   int i;
1333
1334   /* "An m= section is generated for each RtpTransceiver that has been added
1335    * to the Bin, excluding any stopped RtpTransceivers." */
1336   if (trans->stopped)
1337     return FALSE;
1338   if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
1339       || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
1340     return FALSE;
1341
1342   gst_sdp_media_set_port_info (media, 9, 0);
1343   gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
1344   gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
1345
1346   direction =
1347       _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1348       trans->direction);
1349   gst_sdp_media_add_attribute (media, direction, "");
1350   g_free (direction);
1351   /* FIXME: negotiate this */
1352   gst_sdp_media_add_attribute (media, "rtcp-mux", "");
1353   gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
1354
1355   if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
1356     caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
1357     caps = _add_supported_attributes_to_caps (caps);
1358   } else if (type == GST_WEBRTC_SDP_TYPE_ANSWER) {
1359     caps = _find_codec_preferences (webrtc, trans, GST_PAD_SRC, media_idx);
1360     /* FIXME: add rtcp-fb paramaters */
1361   } else {
1362     g_assert_not_reached ();
1363   }
1364
1365   if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
1366     GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
1367     if (caps)
1368       gst_caps_unref (caps);
1369     return FALSE;
1370   }
1371
1372   for (i = 0; i < gst_caps_get_size (caps); i++) {
1373     GstCaps *format = gst_caps_new_empty ();
1374     const GstStructure *s = gst_caps_get_structure (caps, i);
1375
1376     gst_caps_append_structure (format, gst_structure_copy (s));
1377
1378     GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
1379         " to %u-th media", i, format, media_idx);
1380
1381     /* this only looks at the first structure so we loop over the given caps
1382      * and add each structure inside it piecemeal */
1383     gst_sdp_media_set_media_from_caps (format, media);
1384
1385     gst_caps_unref (format);
1386   }
1387
1388   /* Some identifier; we also add the media name to it so it's identifiable */
1389   sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
1390       webrtc->priv->media_counter++);
1391   gst_sdp_media_add_attribute (media, "mid", sdp_mid);
1392   g_free (sdp_mid);
1393
1394   if (trans->sender) {
1395     gchar *cert, *fingerprint, *val;
1396
1397     if (!trans->sender->transport) {
1398       TransportStream *item;
1399       /* FIXME: bundle */
1400       item = _find_transport_for_session (webrtc, media_idx);
1401       if (!item)
1402         item = _create_transport_channel (webrtc, media_idx);
1403       webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
1404     }
1405
1406     g_object_get (trans->sender->transport, "certificate", &cert, NULL);
1407
1408     fingerprint =
1409         _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
1410     g_free (cert);
1411     val =
1412         g_strdup_printf ("%s %s",
1413         _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
1414     g_free (fingerprint);
1415
1416     gst_sdp_media_add_attribute (media, "fingerprint", val);
1417     g_free (val);
1418   }
1419
1420   gst_caps_unref (caps);
1421
1422   return TRUE;
1423 }
1424
1425 static GstSDPMessage *
1426 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
1427 {
1428   GstSDPMessage *ret;
1429   int i;
1430
1431   gst_sdp_message_new (&ret);
1432
1433   gst_sdp_message_set_version (ret, "0");
1434   {
1435     /* FIXME: session id and version need special handling depending on the state we're in */
1436     gchar *sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
1437     gst_sdp_message_set_origin (ret, "-", sess_id, "0", "IN", "IP4", "0.0.0.0");
1438     g_free (sess_id);
1439   }
1440   gst_sdp_message_set_session_name (ret, "-");
1441   gst_sdp_message_add_time (ret, "0", "0", NULL);
1442   gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
1443
1444   /* for each rtp transceiver */
1445   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1446     GstWebRTCRTPTransceiver *trans;
1447     GstSDPMedia media = { 0, };
1448     gchar *ufrag, *pwd;
1449
1450     trans =
1451         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1452         i);
1453
1454     gst_sdp_media_init (&media);
1455     /* mandated by JSEP */
1456     gst_sdp_media_add_attribute (&media, "setup", "actpass");
1457
1458     /* FIXME: only needed when restarting ICE */
1459     _generate_ice_credentials (&ufrag, &pwd);
1460     gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag);
1461     gst_sdp_media_add_attribute (&media, "ice-pwd", pwd);
1462     g_free (ufrag);
1463     g_free (pwd);
1464
1465     if (sdp_media_from_transceiver (webrtc, &media, trans,
1466             GST_WEBRTC_SDP_TYPE_OFFER, i))
1467       gst_sdp_message_add_media (ret, &media);
1468     else
1469       gst_sdp_media_uninit (&media);
1470   }
1471
1472   /* FIXME: pre-emptively setup receiving elements when needed */
1473
1474   /* XXX: only true for the initial offerer */
1475   g_object_set (webrtc->priv->ice, "controller", TRUE, NULL);
1476
1477   return ret;
1478 }
1479
1480 static GstSDPMessage *
1481 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
1482 {
1483   GstSDPMessage *ret = NULL;
1484   const GstWebRTCSessionDescription *pending_remote =
1485       webrtc->pending_remote_description;
1486   int i;
1487
1488   if (!webrtc->pending_remote_description) {
1489     GST_ERROR_OBJECT (webrtc,
1490         "Asked to create an answer without a remote description");
1491     return NULL;
1492   }
1493
1494   gst_sdp_message_new (&ret);
1495
1496   /* FIXME: session id and version need special handling depending on the state we're in */
1497   gst_sdp_message_set_version (ret, "0");
1498   {
1499     const GstSDPOrigin *offer_origin =
1500         gst_sdp_message_get_origin (pending_remote->sdp);
1501     gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id, "0", "IN",
1502         "IP4", "0.0.0.0");
1503   }
1504   gst_sdp_message_set_session_name (ret, "-");
1505
1506   for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
1507     const GstSDPAttribute *attr =
1508         gst_sdp_message_get_attribute (pending_remote->sdp, i);
1509
1510     if (g_strcmp0 (attr->key, "ice-options") == 0) {
1511       gst_sdp_message_add_attribute (ret, attr->key, attr->value);
1512     }
1513   }
1514
1515   for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
1516     /* FIXME:
1517      * bundle policy
1518      */
1519     GstSDPMedia *media = NULL;
1520     GstSDPMedia *offer_media;
1521     GstWebRTCRTPTransceiver *rtp_trans = NULL;
1522     WebRTCTransceiver *trans = NULL;
1523     GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
1524     GstWebRTCDTLSSetup offer_setup, answer_setup;
1525     GstCaps *offer_caps, *answer_caps = NULL;
1526     gchar *cert;
1527     int j;
1528
1529     gst_sdp_media_new (&media);
1530     gst_sdp_media_set_port_info (media, 9, 0);
1531     gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
1532     gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
1533
1534     {
1535       /* FIXME: only needed when restarting ICE */
1536       gchar *ufrag, *pwd;
1537       _generate_ice_credentials (&ufrag, &pwd);
1538       gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
1539       gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
1540       g_free (ufrag);
1541       g_free (pwd);
1542     }
1543
1544     offer_media =
1545         (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
1546     for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
1547       const GstSDPAttribute *attr =
1548           gst_sdp_media_get_attribute (offer_media, j);
1549
1550       if (g_strcmp0 (attr->key, "mid") == 0
1551           || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
1552         gst_sdp_media_add_attribute (media, attr->key, attr->value);
1553         /* FIXME: handle anything we want to keep */
1554       }
1555     }
1556
1557     offer_caps = gst_caps_new_empty ();
1558     for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) {
1559       guint pt = atoi (gst_sdp_media_get_format (offer_media, j));
1560       GstCaps *caps;
1561       int k;
1562
1563       caps = gst_sdp_media_get_caps_from_media (offer_media, pt);
1564
1565       /* gst_sdp_media_get_caps_from_media() produces caps with name
1566        * "application/x-unknown" which will fail intersection with
1567        * "application/x-rtp" caps so mangle the returns caps to have the
1568        * correct name here */
1569       for (k = 0; k < gst_caps_get_size (caps); k++) {
1570         GstStructure *s = gst_caps_get_structure (caps, k);
1571         gst_structure_set_name (s, "application/x-rtp");
1572       }
1573
1574       gst_caps_append (offer_caps, caps);
1575     }
1576
1577     for (j = 0; j < webrtc->priv->transceivers->len; j++) {
1578       GstCaps *trans_caps;
1579
1580       rtp_trans =
1581           g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1582           j);
1583       trans_caps = _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
1584
1585       GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
1586           " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
1587
1588       /* FIXME: technically this is a little overreaching as some fields we
1589        * we can deal with not having and/or we may have unrecognized fields
1590        * that we cannot actually support */
1591       if (trans_caps) {
1592         answer_caps = gst_caps_intersect (offer_caps, trans_caps);
1593         if (answer_caps && !gst_caps_is_empty (answer_caps)) {
1594           GST_LOG_OBJECT (webrtc,
1595               "found compatible transceiver %" GST_PTR_FORMAT
1596               " for offer media %u", trans, i);
1597           if (trans_caps)
1598             gst_caps_unref (trans_caps);
1599           break;
1600         } else {
1601           if (answer_caps) {
1602             gst_caps_unref (answer_caps);
1603             answer_caps = NULL;
1604           }
1605           if (trans_caps)
1606             gst_caps_unref (trans_caps);
1607           rtp_trans = NULL;
1608         }
1609       } else {
1610         rtp_trans = NULL;
1611       }
1612     }
1613
1614     if (rtp_trans) {
1615       answer_dir = rtp_trans->direction;
1616       if (!answer_caps)
1617         goto rejected;
1618     } else {
1619       /* if no transceiver, then we only receive that stream and respond with
1620        * the exact same caps */
1621       /* FIXME: how to validate that subsequent elements can actually receive
1622        * this payload/format */
1623       answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
1624       answer_caps = gst_caps_ref (offer_caps);
1625     }
1626     /* respond with the requested caps */
1627     if (answer_caps) {
1628       gst_sdp_media_set_media_from_caps (answer_caps, media);
1629       gst_caps_unref (answer_caps);
1630       answer_caps = NULL;
1631     }
1632     if (!rtp_trans) {
1633       trans = _create_webrtc_transceiver (webrtc);
1634       rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1635       rtp_trans->direction = answer_dir;
1636       rtp_trans->mline = i;
1637     } else {
1638       trans = WEBRTC_TRANSCEIVER (rtp_trans);
1639     }
1640
1641     /* set the new media direction */
1642     offer_dir = _get_direction_from_media (offer_media);
1643     answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
1644     if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
1645       GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
1646           "transceiver direction");
1647       goto rejected;
1648     }
1649     _media_replace_direction (media, answer_dir);
1650
1651     /* set the a=setup: attribute */
1652     offer_setup = _get_dtls_setup_from_media (offer_media);
1653     answer_setup = _intersect_dtls_setup (offer_setup);
1654     if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
1655       GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
1656           "transceiver direction");
1657       goto rejected;
1658     }
1659     _media_replace_setup (media, answer_setup);
1660
1661     /* FIXME: bundle! */
1662     if (!trans->stream) {
1663       TransportStream *item = _find_transport_for_session (webrtc, i);
1664       if (!item)
1665         item = _create_transport_channel (webrtc, i);
1666       webrtc_transceiver_set_transport (trans, item);
1667     }
1668     /* set the a=fingerprint: for this transport */
1669     g_object_get (trans->stream->transport, "certificate", &cert, NULL);
1670
1671     {
1672       gchar *fingerprint, *val;
1673
1674       fingerprint =
1675           _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
1676       g_free (cert);
1677       val =
1678           g_strdup_printf ("%s %s",
1679           _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
1680       g_free (fingerprint);
1681
1682       gst_sdp_media_add_attribute (media, "fingerprint", val);
1683       g_free (val);
1684     }
1685
1686     if (0) {
1687     rejected:
1688       GST_INFO_OBJECT (webrtc, "media %u rejected", i);
1689       gst_sdp_media_free (media);
1690       gst_sdp_media_copy (offer_media, &media);
1691       gst_sdp_media_set_port_info (media, 0, 0);
1692     }
1693     gst_sdp_message_add_media (ret, media);
1694     gst_sdp_media_free (media);
1695
1696     gst_caps_unref (offer_caps);
1697   }
1698
1699   /* FIXME: can we add not matched transceivers? */
1700
1701   /* XXX: only true for the initial offerer */
1702   g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
1703
1704   return ret;
1705 }
1706
1707 struct create_sdp
1708 {
1709   GstStructure *options;
1710   GstPromise *promise;
1711   GstWebRTCSDPType type;
1712 };
1713
1714 static void
1715 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
1716 {
1717   GstWebRTCSessionDescription *desc = NULL;
1718   GstSDPMessage *sdp = NULL;
1719   GstStructure *s = NULL;
1720
1721   GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
1722       gst_webrtc_sdp_type_to_string (data->type), data->options);
1723
1724   if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
1725     sdp = _create_offer_task (webrtc, data->options);
1726   else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
1727     sdp = _create_answer_task (webrtc, data->options);
1728   else {
1729     g_assert_not_reached ();
1730     goto out;
1731   }
1732
1733   if (sdp) {
1734     desc = gst_webrtc_session_description_new (data->type, sdp);
1735     s = gst_structure_new ("application/x-gst-promise",
1736         gst_webrtc_sdp_type_to_string (data->type),
1737         GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
1738   }
1739
1740 out:
1741   PC_UNLOCK (webrtc);
1742   gst_promise_reply (data->promise, s);
1743   PC_LOCK (webrtc);
1744
1745   if (desc)
1746     gst_webrtc_session_description_free (desc);
1747 }
1748
1749 static void
1750 _free_create_sdp_data (struct create_sdp *data)
1751 {
1752   if (data->options)
1753     gst_structure_free (data->options);
1754   gst_promise_unref (data->promise);
1755   g_free (data);
1756 }
1757
1758 static void
1759 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
1760     const GstStructure * options, GstPromise * promise)
1761 {
1762   struct create_sdp *data = g_new0 (struct create_sdp, 1);
1763
1764   if (options)
1765     data->options = gst_structure_copy (options);
1766   data->promise = gst_promise_ref (promise);
1767   data->type = GST_WEBRTC_SDP_TYPE_OFFER;
1768
1769   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
1770       data, (GDestroyNotify) _free_create_sdp_data);
1771 }
1772
1773 static void
1774 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
1775     const GstStructure * options, GstPromise * promise)
1776 {
1777   struct create_sdp *data = g_new0 (struct create_sdp, 1);
1778
1779   if (options)
1780     data->options = gst_structure_copy (options);
1781   data->promise = gst_promise_ref (promise);
1782   data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
1783
1784   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
1785       data, (GDestroyNotify) _free_create_sdp_data);
1786 }
1787
1788 static GstWebRTCBinPad *
1789 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
1790     guint media_idx)
1791 {
1792   GstWebRTCBinPad *pad;
1793   gchar *pad_name;
1794
1795   pad_name =
1796       g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
1797       media_idx);
1798   pad = gst_webrtc_bin_pad_new (pad_name, direction);
1799   g_free (pad_name);
1800   pad->mlineindex = media_idx;
1801
1802   return pad;
1803 }
1804
1805 static GstWebRTCRTPTransceiver *
1806 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
1807     const GstSDPMessage * sdp, guint media_idx)
1808 {
1809   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
1810   GstWebRTCRTPTransceiver *ret = NULL;
1811   int i;
1812
1813   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
1814     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
1815
1816     if (g_strcmp0 (attr->key, "mid") == 0) {
1817       if ((ret =
1818               _find_transceiver (webrtc, attr->value,
1819                   (FindTransceiverFunc) match_for_mid)))
1820         goto out;
1821     }
1822   }
1823
1824   ret = _find_transceiver (webrtc, &media_idx,
1825       (FindTransceiverFunc) transceiver_match_for_mline);
1826
1827 out:
1828   GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
1829   return ret;
1830 }
1831
1832 static GstPad *
1833 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
1834 {
1835 /*
1836  * ,-------------------------webrtcbin-------------------------,
1837  * ;                                                           ;
1838  * ;          ,-------rtpbin-------,   ,--transport_send_%u--, ;
1839  * ;          ;    send_rtp_src_%u o---o rtp_sink            ; ;
1840  * ;          ;                    ;   ;                     ; ;
1841  * ;          ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
1842  * ; sink_%u  ;                    ;   '---------------------' ;
1843  * o----------o send_rtp_sink_%u   ;                           ;
1844  * ;          '--------------------'                           ;
1845  * '--------------------- -------------------------------------'
1846  */
1847   GstPadTemplate *rtp_templ;
1848   GstPad *rtp_sink;
1849   gchar *pad_name;
1850   WebRTCTransceiver *trans;
1851
1852   g_return_val_if_fail (pad->trans != NULL, NULL);
1853
1854   GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
1855
1856   rtp_templ =
1857       _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
1858       "send_rtp_sink_%u");
1859   g_assert (rtp_templ);
1860
1861   pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
1862   rtp_sink =
1863       gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
1864   g_free (pad_name);
1865   gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
1866   gst_object_unref (rtp_sink);
1867
1868   trans = WEBRTC_TRANSCEIVER (pad->trans);
1869   if (!trans->stream) {
1870     TransportStream *item;
1871     /* FIXME: bundle */
1872     item = _find_transport_for_session (webrtc, pad->mlineindex);
1873     if (!item)
1874       item = _create_transport_channel (webrtc, pad->mlineindex);
1875     webrtc_transceiver_set_transport (trans, item);
1876   }
1877
1878   pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
1879   if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1880           GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
1881     g_warn_if_reached ();
1882   g_free (pad_name);
1883
1884   gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
1885
1886   return GST_PAD (pad);
1887 }
1888
1889 /* output pads are receiving elements */
1890 static GstWebRTCBinPad *
1891 _connect_output_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
1892 {
1893 /*
1894  * ,------------------------webrtcbin------------------------,
1895  * ;                             ,---------rtpbin---------,  ;
1896  * ; ,-transport_receive_%u--,   ;                        ;  ;
1897  * ; ;               rtp_src o---o recv_rtp_sink_%u       ;  ;
1898  * ; ;                       ;   ;                        ;  ;
1899  * ; ;              rtcp_src o---o recv_rtcp_sink_%u      ;  ;
1900  * ; '-----------------------'   ;                        ;  ; src_%u
1901  * ;                             ;  recv_rtp_src_%u_%u_%u o--o
1902  * ;                             '------------------------'  ;
1903  * '---------------------------------------------------------'
1904  */
1905   gchar *pad_name;
1906   WebRTCTransceiver *trans;
1907
1908   g_return_val_if_fail (pad->trans != NULL, NULL);
1909
1910   GST_INFO_OBJECT (pad, "linking output stream %u", pad->mlineindex);
1911
1912   trans = WEBRTC_TRANSCEIVER (pad->trans);
1913   if (!trans->stream) {
1914     TransportStream *item;
1915     /* FIXME: bundle */
1916     item = _find_transport_for_session (webrtc, pad->mlineindex);
1917     if (!item)
1918       item = _create_transport_channel (webrtc, pad->mlineindex);
1919     webrtc_transceiver_set_transport (trans, item);
1920   }
1921
1922   pad_name = g_strdup_printf ("recv_rtp_sink_%u", pad->mlineindex);
1923   if (!gst_element_link_pads (GST_ELEMENT (trans->stream->receive_bin),
1924           "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
1925     g_warn_if_reached ();
1926   g_free (pad_name);
1927
1928   gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->receive_bin));
1929
1930   return pad;
1931 }
1932
1933 typedef struct
1934 {
1935   guint mlineindex;
1936   gchar *candidate;
1937 } IceCandidateItem;
1938
1939 static void
1940 _clear_ice_candidate_item (IceCandidateItem ** item)
1941 {
1942   g_free ((*item)->candidate);
1943   g_free (*item);
1944 }
1945
1946 static void
1947 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item)
1948 {
1949   GstWebRTCICEStream *stream;
1950
1951   stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
1952   if (stream == NULL) {
1953     GST_WARNING_OBJECT (webrtc, "Unknown mline %u, ignoring", item->mlineindex);
1954     return;
1955   }
1956
1957   GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
1958       item->mlineindex, item->candidate);
1959
1960   gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
1961 }
1962
1963 static void
1964 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
1965     const GstSDPMessage * sdp, guint media_idx,
1966     GstWebRTCRTPTransceiver * rtp_trans)
1967 {
1968   WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1969   TransportStream *stream = trans->stream;
1970   GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
1971   GstWebRTCRTPTransceiverDirection new_dir;
1972   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
1973   GstWebRTCDTLSSetup new_setup;
1974   gboolean new_rtcp_mux, new_rtcp_rsize;
1975   int i;
1976
1977   rtp_trans->mline = media_idx;
1978
1979   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
1980     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
1981
1982     if (g_strcmp0 (attr->key, "mid") == 0) {
1983       g_free (rtp_trans->mid);
1984       rtp_trans->mid = g_strdup (attr->value);
1985     }
1986   }
1987
1988   if (!stream) {
1989     /* FIXME: find an existing transport for e.g. bundle/reconfiguration */
1990     stream = _find_transport_for_session (webrtc, media_idx);
1991     if (!stream)
1992       stream = _create_transport_channel (webrtc, media_idx);
1993     webrtc_transceiver_set_transport (trans, stream);
1994   }
1995
1996   {
1997     const GstSDPMedia *local_media, *remote_media;
1998     GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1999     GstWebRTCDTLSSetup local_setup, remote_setup;
2000     guint i, len;
2001     const gchar *proto;
2002     GstCaps *global_caps;
2003
2004     local_media =
2005         gst_sdp_message_get_media (webrtc->current_local_description->sdp,
2006         media_idx);
2007     remote_media =
2008         gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
2009         media_idx);
2010
2011     local_setup = _get_dtls_setup_from_media (local_media);
2012     remote_setup = _get_dtls_setup_from_media (remote_media);
2013     new_setup = _get_final_setup (local_setup, remote_setup);
2014     if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
2015       return;
2016
2017     local_dir = _get_direction_from_media (local_media);
2018     remote_dir = _get_direction_from_media (remote_media);
2019     new_dir = _get_final_direction (local_dir, remote_dir);
2020     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
2021       return;
2022
2023     /* get proto */
2024     proto = gst_sdp_media_get_proto (media);
2025     if (proto != NULL) {
2026       /* Parse global SDP attributes once */
2027       global_caps = gst_caps_new_empty_simple ("application/x-unknown");
2028       GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
2029       gst_sdp_message_attributes_to_caps (sdp, global_caps);
2030       GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
2031       gst_sdp_media_attributes_to_caps (media, global_caps);
2032
2033       /* clear the ptmap */
2034       g_array_set_size (stream->ptmap, 0);
2035
2036       len = gst_sdp_media_formats_len (media);
2037       for (i = 0; i < len; i++) {
2038         GstCaps *caps, *outcaps;
2039         GstStructure *s;
2040         PtMapItem item;
2041         gint pt;
2042
2043         pt = atoi (gst_sdp_media_get_format (media, i));
2044
2045         GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
2046
2047         /* convert caps */
2048         caps = gst_sdp_media_get_caps_from_media (media, pt);
2049         if (caps == NULL) {
2050           GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
2051           continue;
2052         }
2053
2054         /* Merge in global caps */
2055         /* Intersect will merge in missing fields to the current caps */
2056         outcaps = gst_caps_intersect (caps, global_caps);
2057         gst_caps_unref (caps);
2058
2059         s = gst_caps_get_structure (outcaps, 0);
2060         gst_structure_set_name (s, "application/x-rtp");
2061
2062         item.pt = pt;
2063         item.caps = outcaps;
2064
2065         g_array_append_val (stream->ptmap, item);
2066       }
2067
2068       gst_caps_unref (global_caps);
2069     }
2070
2071     new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
2072         && _media_has_attribute_key (remote_media, "rtcp-mux");
2073     new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
2074         && _media_has_attribute_key (remote_media, "rtcp-rsize");
2075
2076     {
2077       GObject *session;
2078       g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
2079           media_idx, &session);
2080       if (session) {
2081         g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
2082         g_object_unref (session);
2083       }
2084     }
2085   }
2086
2087   if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2088       && prev_dir != new_dir) {
2089     GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
2090     return;
2091   }
2092
2093   /* FIXME: bundle! */
2094   g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
2095
2096   if (new_dir != prev_dir) {
2097     TransportReceiveBin *receive;
2098
2099     GST_TRACE_OBJECT (webrtc, "transceiver direction change");
2100
2101     /* FIXME: this may not always be true. e.g. bundle */
2102     g_assert (media_idx == stream->session_id);
2103
2104     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
2105         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2106       GstWebRTCBinPad *pad =
2107           _find_pad_for_mline (webrtc, GST_PAD_SINK, media_idx);
2108       if (pad) {
2109         GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
2110             " for transceiver %" GST_PTR_FORMAT, pad, trans);
2111         g_assert (pad->trans == rtp_trans);
2112         g_assert (pad->mlineindex == media_idx);
2113         gst_object_unref (pad);
2114       } else {
2115         GST_DEBUG_OBJECT (webrtc,
2116             "creating new pad send pad for transceiver %" GST_PTR_FORMAT,
2117             trans);
2118         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, media_idx);
2119         pad->trans = gst_object_ref (rtp_trans);
2120         _connect_input_stream (webrtc, pad);
2121         _add_pad (webrtc, pad);
2122       }
2123       g_object_set (stream, "dtls-client",
2124           new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
2125     }
2126     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
2127         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2128       GstWebRTCBinPad *pad =
2129           _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
2130       if (pad) {
2131         GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
2132             " for transceiver %" GST_PTR_FORMAT, pad, trans);
2133         g_assert (pad->trans == rtp_trans);
2134         g_assert (pad->mlineindex == media_idx);
2135         gst_object_unref (pad);
2136       } else {
2137         GST_DEBUG_OBJECT (webrtc,
2138             "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
2139         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
2140         pad->trans = gst_object_ref (rtp_trans);
2141         _connect_output_stream (webrtc, pad);
2142         /* delay adding the pad until rtpbin creates the recv output pad
2143          * to ghost to so queries/events travel through the pipeline correctly
2144          * as soon as the pad is added */
2145         _add_pad_to_list (webrtc, pad);
2146       }
2147       g_object_set (stream, "dtls-client",
2148           new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
2149     }
2150
2151     receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
2152     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
2153         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV)
2154       transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
2155     else
2156       transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_DROP);
2157
2158     rtp_trans->mline = media_idx;
2159     rtp_trans->current_direction = new_dir;
2160   }
2161 }
2162
2163 static gboolean
2164 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
2165     gconstpointer data)
2166 {
2167   if (p1->mid)
2168     return FALSE;
2169   if (p1->mline != -1)
2170     return FALSE;
2171
2172   return TRUE;
2173 }
2174
2175 static gboolean
2176 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
2177     GstWebRTCSessionDescription * sdp)
2178 {
2179   int i;
2180
2181   for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
2182     const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
2183     GstWebRTCRTPTransceiver *trans;
2184
2185     /* skip rejected media */
2186     if (gst_sdp_media_get_port (media) == 0)
2187       continue;
2188
2189     trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
2190
2191     if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
2192       GST_ERROR ("State mismatch.  Could not find local transceiver by mline.");
2193       return FALSE;
2194     } else {
2195       if (trans) {
2196         _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, trans);
2197       } else {
2198         trans = _find_transceiver (webrtc, NULL,
2199             (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
2200         if (!trans)
2201           trans =
2202               GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc));
2203         /* XXX: default to the advertised direction in the sdp for new
2204          * transceviers.  The spec doesn't actually say what happens here, only
2205          * that calls to setDirection will change the value.  Nothing about
2206          * a default value when the transceiver is created internally */
2207         trans->direction = _get_direction_from_media (media);
2208         _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, trans);
2209       }
2210     }
2211   }
2212
2213   return TRUE;
2214 }
2215
2216 static void
2217 _get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx,
2218     gchar ** ufrag, gchar ** pwd)
2219 {
2220   int i;
2221
2222   *ufrag = NULL;
2223   *pwd = NULL;
2224
2225   {
2226     /* search in the corresponding media section */
2227     const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
2228     const gchar *tmp_ufrag =
2229         gst_sdp_media_get_attribute_val (media, "ice-ufrag");
2230     const gchar *tmp_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
2231     if (tmp_ufrag && tmp_pwd) {
2232       *ufrag = g_strdup (tmp_ufrag);
2233       *pwd = g_strdup (tmp_pwd);
2234       return;
2235     }
2236   }
2237
2238   /* then in the sdp message itself */
2239   for (i = 0; i < gst_sdp_message_attributes_len (sdp); i++) {
2240     const GstSDPAttribute *attr = gst_sdp_message_get_attribute (sdp, i);
2241
2242     if (g_strcmp0 (attr->key, "ice-ufrag") == 0) {
2243       g_assert (!*ufrag);
2244       *ufrag = g_strdup (attr->value);
2245     } else if (g_strcmp0 (attr->key, "ice-pwd") == 0) {
2246       g_assert (!*pwd);
2247       *pwd = g_strdup (attr->value);
2248     }
2249   }
2250   if (!*ufrag && !*pwd) {
2251     /* Check in the medias themselves. According to JSEP, they should be
2252      * identical FIXME: only for bundle-d streams */
2253     for (i = 0; i < gst_sdp_message_medias_len (sdp); i++) {
2254       const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i);
2255       const gchar *tmp_ufrag =
2256           gst_sdp_media_get_attribute_val (media, "ice-ufrag");
2257       const gchar *tmp_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
2258       if (tmp_ufrag && tmp_pwd) {
2259         *ufrag = g_strdup (tmp_ufrag);
2260         *pwd = g_strdup (tmp_pwd);
2261         break;
2262       }
2263     }
2264   }
2265 }
2266
2267 struct set_description
2268 {
2269   GstPromise *promise;
2270   SDPSource source;
2271   GstWebRTCSessionDescription *sdp;
2272 };
2273
2274 /* http://w3c.github.io/webrtc-pc/#set-description */
2275 static void
2276 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
2277 {
2278   GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
2279   GError *error = NULL;
2280
2281   {
2282     gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
2283         webrtc->signaling_state);
2284     gchar *type_str =
2285         _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
2286     gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
2287     GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
2288         _sdp_source_to_string (sd->source), type_str, state);
2289     GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
2290     g_free (sdp_text);
2291     g_free (state);
2292     g_free (type_str);
2293   }
2294
2295   if (!validate_sdp (webrtc, sd->source, sd->sdp, &error)) {
2296     GST_ERROR_OBJECT (webrtc, "%s", error->message);
2297     goto out;
2298   }
2299
2300   if (webrtc->priv->is_closed) {
2301     GST_WARNING_OBJECT (webrtc, "we are closed");
2302     goto out;
2303   }
2304
2305   switch (sd->sdp->type) {
2306     case GST_WEBRTC_SDP_TYPE_OFFER:{
2307       if (sd->source == SDP_LOCAL) {
2308         if (webrtc->pending_local_description)
2309           gst_webrtc_session_description_free
2310               (webrtc->pending_local_description);
2311         webrtc->pending_local_description =
2312             gst_webrtc_session_description_copy (sd->sdp);
2313         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
2314       } else {
2315         if (webrtc->pending_remote_description)
2316           gst_webrtc_session_description_free
2317               (webrtc->pending_remote_description);
2318         webrtc->pending_remote_description =
2319             gst_webrtc_session_description_copy (sd->sdp);
2320         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
2321       }
2322       break;
2323     }
2324     case GST_WEBRTC_SDP_TYPE_ANSWER:{
2325       if (sd->source == SDP_LOCAL) {
2326         if (webrtc->current_local_description)
2327           gst_webrtc_session_description_free
2328               (webrtc->current_local_description);
2329         webrtc->current_local_description =
2330             gst_webrtc_session_description_copy (sd->sdp);
2331
2332         if (webrtc->current_remote_description)
2333           gst_webrtc_session_description_free
2334               (webrtc->current_remote_description);
2335         webrtc->current_remote_description = webrtc->pending_remote_description;
2336         webrtc->pending_remote_description = NULL;
2337       } else {
2338         if (webrtc->current_remote_description)
2339           gst_webrtc_session_description_free
2340               (webrtc->current_remote_description);
2341         webrtc->current_remote_description =
2342             gst_webrtc_session_description_copy (sd->sdp);
2343
2344         if (webrtc->current_local_description)
2345           gst_webrtc_session_description_free
2346               (webrtc->current_local_description);
2347         webrtc->current_local_description = webrtc->pending_local_description;
2348         webrtc->pending_local_description = NULL;
2349       }
2350
2351       if (webrtc->pending_local_description)
2352         gst_webrtc_session_description_free (webrtc->pending_local_description);
2353       webrtc->pending_local_description = NULL;
2354
2355       if (webrtc->pending_remote_description)
2356         gst_webrtc_session_description_free
2357             (webrtc->pending_remote_description);
2358       webrtc->pending_remote_description = NULL;
2359
2360       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
2361       break;
2362     }
2363     case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
2364       GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
2365       if (sd->source == SDP_LOCAL) {
2366         if (webrtc->pending_local_description)
2367           gst_webrtc_session_description_free
2368               (webrtc->pending_local_description);
2369         webrtc->pending_local_description = NULL;
2370       } else {
2371         if (webrtc->pending_remote_description)
2372           gst_webrtc_session_description_free
2373               (webrtc->pending_remote_description);
2374         webrtc->pending_remote_description = NULL;
2375       }
2376
2377       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
2378       break;
2379     }
2380     case GST_WEBRTC_SDP_TYPE_PRANSWER:{
2381       GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
2382       if (sd->source == SDP_LOCAL) {
2383         if (webrtc->pending_local_description)
2384           gst_webrtc_session_description_free
2385               (webrtc->pending_local_description);
2386         webrtc->pending_local_description =
2387             gst_webrtc_session_description_copy (sd->sdp);
2388
2389         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
2390       } else {
2391         if (webrtc->pending_remote_description)
2392           gst_webrtc_session_description_free
2393               (webrtc->pending_remote_description);
2394         webrtc->pending_remote_description =
2395             gst_webrtc_session_description_copy (sd->sdp);
2396
2397         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
2398       }
2399       break;
2400     }
2401   }
2402
2403   if (new_signaling_state != webrtc->signaling_state) {
2404     gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
2405         webrtc->signaling_state);
2406     gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
2407         new_signaling_state);
2408     GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
2409         "to %s", from, to);
2410     webrtc->signaling_state = new_signaling_state;
2411     PC_UNLOCK (webrtc);
2412     g_object_notify (G_OBJECT (webrtc), "signaling-state");
2413     PC_LOCK (webrtc);
2414
2415     g_free (from);
2416     g_free (to);
2417   }
2418
2419   /* TODO: necessary data channel modifications */
2420
2421   if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
2422     /* FIXME:
2423      * If the mid value of an RTCRtpTransceiver was set to a non-null value 
2424      * by the RTCSessionDescription that is being rolled back, set the mid
2425      * value of that transceiver to null, as described by [JSEP]
2426      * (section 4.1.7.2.).
2427      * If an RTCRtpTransceiver was created by applying the
2428      * RTCSessionDescription that is being rolled back, and a track has not
2429      * been attached to it via addTrack, remove that transceiver from
2430      * connection's set of transceivers, as described by [JSEP]
2431      * (section 4.1.7.2.).
2432      * Restore the value of connection's [[ sctpTransport]] internal slot
2433      * to its value at the last stable signaling state.
2434      */
2435   }
2436
2437   if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
2438     gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
2439
2440     /* media modifications */
2441     _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp);
2442
2443     /* If connection's signaling state is now stable, update the
2444      * negotiation-needed flag. If connection's [[ needNegotiation]] slot
2445      * was true both before and after this update, queue a task to check
2446      * connection's [[needNegotiation]] slot and, if still true, fire a
2447      * simple event named negotiationneeded at connection.*/
2448     _update_need_negotiation (webrtc);
2449     if (prev_need_negotiation && webrtc->priv->need_negotiation) {
2450       _check_need_negotiation_task (webrtc, NULL);
2451     }
2452   }
2453
2454   if (sd->source == SDP_LOCAL) {
2455     int i;
2456
2457     for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
2458       gchar *ufrag, *pwd;
2459       TransportStream *item;
2460
2461       /* FIXME: bundle */
2462       item = _find_transport_for_session (webrtc, i);
2463       if (!item)
2464         item = _create_transport_channel (webrtc, i);
2465
2466       _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
2467       gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
2468           item->stream, ufrag, pwd);
2469       g_free (ufrag);
2470       g_free (pwd);
2471     }
2472   }
2473
2474   if (sd->source == SDP_REMOTE) {
2475     int i;
2476
2477     for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
2478       gchar *ufrag, *pwd;
2479       TransportStream *item;
2480
2481       /* FIXME: bundle */
2482       item = _find_transport_for_session (webrtc, i);
2483       if (!item)
2484         item = _create_transport_channel (webrtc, i);
2485
2486       _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
2487       gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
2488           item->stream, ufrag, pwd);
2489       g_free (ufrag);
2490       g_free (pwd);
2491     }
2492   }
2493
2494   {
2495     int i;
2496     for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
2497       IceStreamItem *item =
2498           &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
2499
2500       gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
2501     }
2502   }
2503
2504   if (webrtc->current_local_description && webrtc->current_remote_description) {
2505     int i;
2506
2507     for (i = 0; i < webrtc->priv->pending_ice_candidates->len; i++) {
2508       IceCandidateItem *item =
2509           g_array_index (webrtc->priv->pending_ice_candidates,
2510           IceCandidateItem *, i);
2511
2512       _add_ice_candidate (webrtc, item);
2513     }
2514     g_array_set_size (webrtc->priv->pending_ice_candidates, 0);
2515   }
2516
2517 out:
2518   PC_UNLOCK (webrtc);
2519   gst_promise_reply (sd->promise, NULL);
2520   PC_LOCK (webrtc);
2521 }
2522
2523 static void
2524 _free_set_description_data (struct set_description *sd)
2525 {
2526   if (sd->promise)
2527     gst_promise_unref (sd->promise);
2528   if (sd->sdp)
2529     gst_webrtc_session_description_free (sd->sdp);
2530   g_free (sd);
2531 }
2532
2533 static void
2534 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
2535     GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
2536 {
2537   struct set_description *sd;
2538
2539   if (remote_sdp == NULL)
2540     goto bad_input;
2541   if (remote_sdp->sdp == NULL)
2542     goto bad_input;
2543
2544   sd = g_new0 (struct set_description, 1);
2545   if (promise != NULL)
2546     sd->promise = gst_promise_ref (promise);
2547   sd->source = SDP_REMOTE;
2548   sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
2549
2550   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
2551       sd, (GDestroyNotify) _free_set_description_data);
2552
2553   return;
2554
2555 bad_input:
2556   {
2557     gst_promise_reply (promise, NULL);
2558     g_return_if_reached ();
2559   }
2560 }
2561
2562 static void
2563 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
2564     GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
2565 {
2566   struct set_description *sd;
2567
2568   if (local_sdp == NULL)
2569     goto bad_input;
2570   if (local_sdp->sdp == NULL)
2571     goto bad_input;
2572
2573   sd = g_new0 (struct set_description, 1);
2574   if (promise != NULL)
2575     sd->promise = gst_promise_ref (promise);
2576   sd->source = SDP_LOCAL;
2577   sd->sdp = gst_webrtc_session_description_copy (local_sdp);
2578
2579   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
2580       sd, (GDestroyNotify) _free_set_description_data);
2581
2582   return;
2583
2584 bad_input:
2585   {
2586     gst_promise_reply (promise, NULL);
2587     g_return_if_reached ();
2588   }
2589 }
2590
2591 static void
2592 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
2593 {
2594   if (!webrtc->current_local_description || !webrtc->current_remote_description) {
2595     IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
2596     new->mlineindex = item->mlineindex;
2597     new->candidate = g_strdup (item->candidate);
2598
2599     g_array_append_val (webrtc->priv->pending_ice_candidates, new);
2600   } else {
2601     _add_ice_candidate (webrtc, item);
2602   }
2603 }
2604
2605 static void
2606 _free_ice_candidate_item (IceCandidateItem * item)
2607 {
2608   _clear_ice_candidate_item (&item);
2609 }
2610
2611 static void
2612 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
2613     const gchar * attr)
2614 {
2615   IceCandidateItem *item;
2616
2617   item = g_new0 (IceCandidateItem, 1);
2618   item->mlineindex = mline;
2619   if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
2620     item->candidate = g_strdup (attr);
2621   else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
2622     item->candidate = g_strdup_printf ("a=%s", attr);
2623   gst_webrtc_bin_enqueue_task (webrtc,
2624       (GstWebRTCBinFunc) _add_ice_candidate_task, item,
2625       (GDestroyNotify) _free_ice_candidate_item);
2626 }
2627
2628 static void
2629 _on_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
2630 {
2631   const gchar *cand = item->candidate;
2632
2633   if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
2634     /* stripping away "a=" */
2635     cand += 2;
2636   }
2637
2638   GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
2639       item->mlineindex, cand);
2640
2641   PC_UNLOCK (webrtc);
2642   g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
2643       0, item->mlineindex, cand);
2644   PC_LOCK (webrtc);
2645 }
2646
2647 static void
2648 _on_ice_candidate (GstWebRTCICE * ice, guint session_id,
2649     gchar * candidate, GstWebRTCBin * webrtc)
2650 {
2651   IceCandidateItem *item = g_new0 (IceCandidateItem, 1);
2652
2653   /* FIXME: bundle support */
2654   item->mlineindex = session_id;
2655   item->candidate = g_strdup (candidate);
2656
2657   gst_webrtc_bin_enqueue_task (webrtc,
2658       (GstWebRTCBinFunc) _on_ice_candidate_task, item,
2659       (GDestroyNotify) _free_ice_candidate_item);
2660 }
2661
2662 /* https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm */
2663 static GstStructure *
2664 _get_stats_from_selector (GstWebRTCBin * webrtc, gpointer selector)
2665 {
2666   if (selector)
2667     GST_FIXME_OBJECT (webrtc, "Implement stats selection");
2668
2669   return gst_structure_copy (webrtc->priv->stats);
2670 }
2671
2672 struct get_stats
2673 {
2674   GstPad *pad;
2675   GstPromise *promise;
2676 };
2677
2678 static void
2679 _free_get_stats (struct get_stats *stats)
2680 {
2681   if (stats->pad)
2682     gst_object_unref (stats->pad);
2683   if (stats->promise)
2684     gst_promise_unref (stats->promise);
2685   g_free (stats);
2686 }
2687
2688 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
2689 static void
2690 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
2691 {
2692   GstStructure *s;
2693   gpointer selector = NULL;
2694
2695   gst_webrtc_bin_update_stats (webrtc);
2696
2697   if (stats->pad) {
2698     GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (stats->pad);
2699
2700     if (wpad->trans) {
2701       if (GST_PAD_DIRECTION (wpad) == GST_PAD_SRC) {
2702         selector = wpad->trans->receiver;
2703       } else {
2704         selector = wpad->trans->sender;
2705       }
2706     }
2707   }
2708
2709   s = _get_stats_from_selector (webrtc, selector);
2710   gst_promise_reply (stats->promise, s);
2711 }
2712
2713 static void
2714 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
2715     GstPromise * promise)
2716 {
2717   struct get_stats *stats;
2718
2719   g_return_if_fail (promise != NULL);
2720   g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
2721
2722   stats = g_new0 (struct get_stats, 1);
2723   stats->promise = gst_promise_ref (promise);
2724   /* FIXME: check that pad exists in element */
2725   if (pad)
2726     stats->pad = gst_object_ref (pad);
2727
2728   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
2729       stats, (GDestroyNotify) _free_get_stats);
2730 }
2731
2732 static GstWebRTCRTPTransceiver *
2733 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
2734     GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
2735 {
2736   WebRTCTransceiver *trans;
2737   GstWebRTCRTPTransceiver *rtp_trans;
2738
2739   g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
2740       NULL);
2741
2742   trans = _create_webrtc_transceiver (webrtc);
2743   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2744   rtp_trans->direction = direction;
2745   if (caps)
2746     rtp_trans->codec_preferences = gst_caps_ref (caps);
2747
2748   return gst_object_ref (trans);
2749 }
2750
2751 static void
2752 _deref_and_unref (GstObject ** object)
2753 {
2754   if (object)
2755     gst_object_unref (*object);
2756 }
2757
2758 static GArray *
2759 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
2760 {
2761   GArray *arr = g_array_new (FALSE, TRUE, sizeof (gpointer));
2762   int i;
2763
2764   g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
2765
2766   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2767     GstWebRTCRTPTransceiver *trans =
2768         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
2769         i);
2770     gst_object_ref (trans);
2771     g_array_append_val (arr, trans);
2772   }
2773
2774   return arr;
2775 }
2776
2777 /* === rtpbin signal implementations === */
2778
2779 static void
2780 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
2781     GstWebRTCBin * webrtc)
2782 {
2783   gchar *new_pad_name = NULL;
2784
2785   new_pad_name = gst_pad_get_name (new_pad);
2786   GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
2787   if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
2788     guint32 session_id = 0, ssrc = 0, pt = 0;
2789     GstWebRTCRTPTransceiver *rtp_trans;
2790     WebRTCTransceiver *trans;
2791     TransportStream *stream;
2792     GstWebRTCBinPad *pad;
2793
2794     if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc, &pt)) {
2795       g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
2796       return;
2797     }
2798
2799     stream = _find_transport_for_session (webrtc, session_id);
2800     if (!stream)
2801       g_warn_if_reached ();
2802
2803     /* FIXME: bundle! */
2804     rtp_trans = _find_transceiver_for_mline (webrtc, session_id);
2805     if (!rtp_trans)
2806       g_warn_if_reached ();
2807     trans = WEBRTC_TRANSCEIVER (rtp_trans);
2808     g_assert (trans->stream == stream);
2809
2810     pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
2811
2812     GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
2813         " for rtpbin pad name %s", pad, new_pad_name);
2814     if (!pad)
2815       g_warn_if_reached ();
2816     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
2817
2818     if (webrtc->priv->running)
2819       gst_pad_set_active (GST_PAD (pad), TRUE);
2820     gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
2821     _remove_pending_pad (webrtc, pad);
2822
2823     gst_object_unref (pad);
2824   }
2825   g_free (new_pad_name);
2826 }
2827
2828 /* only used for the receiving streams */
2829 static GstCaps *
2830 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
2831     GstWebRTCBin * webrtc)
2832 {
2833   TransportStream *stream;
2834   GstCaps *ret;
2835
2836   GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
2837       session_id);
2838
2839   stream = _find_transport_for_session (webrtc, session_id);
2840   if (!stream)
2841     goto unknown_session;
2842
2843   if ((ret = _transport_stream_get_caps_for_pt (stream, pt)))
2844     gst_caps_ref (ret);
2845
2846   GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
2847       "session %d", ret, pt, session_id);
2848
2849   return ret;
2850
2851 unknown_session:
2852   {
2853     GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
2854     return NULL;
2855   }
2856 }
2857
2858 static GstElement *
2859 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
2860     GstWebRTCBin * webrtc)
2861 {
2862   return NULL;
2863 }
2864
2865 static GstElement *
2866 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
2867     GstWebRTCBin * webrtc)
2868 {
2869   return NULL;
2870 }
2871
2872 static void
2873 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
2874     GstWebRTCBin * webrtc)
2875 {
2876 }
2877
2878 static void
2879 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
2880     guint session_id, guint ssrc, GstWebRTCBin * webrtc)
2881 {
2882 }
2883
2884 static GstElement *
2885 _create_rtpbin (GstWebRTCBin * webrtc)
2886 {
2887   GstElement *rtpbin;
2888
2889   if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
2890     return NULL;
2891
2892   /* mandated by WebRTC */
2893   gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
2894
2895   g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
2896       webrtc);
2897   g_signal_connect (rtpbin, "request-pt-map",
2898       G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
2899   g_signal_connect (rtpbin, "request-aux-sender",
2900       G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
2901   g_signal_connect (rtpbin, "request-aux-receiver",
2902       G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
2903   g_signal_connect (rtpbin, "on-ssrc-active",
2904       G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
2905   g_signal_connect (rtpbin, "new-jitterbuffer",
2906       G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
2907
2908   return rtpbin;
2909 }
2910
2911 static GstStateChangeReturn
2912 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
2913 {
2914   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
2915   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2916
2917   GST_DEBUG ("changing state: %s => %s",
2918       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
2919       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
2920
2921   switch (transition) {
2922     case GST_STATE_CHANGE_NULL_TO_READY:{
2923       GstElement *nice;
2924       if (!webrtc->rtpbin) {
2925         /* FIXME: is this the right thing for a missing plugin? */
2926         GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, (NULL),
2927             ("%s", "rtpbin element is not available"));
2928         return GST_STATE_CHANGE_FAILURE;
2929       }
2930       nice = gst_element_factory_make ("nicesrc", NULL);
2931       if (!nice) {
2932         /* FIXME: is this the right thing for a missing plugin? */
2933         GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, (NULL),
2934             ("%s", "libnice elements are not available"));
2935         return GST_STATE_CHANGE_FAILURE;
2936       }
2937       gst_object_unref (nice);
2938       nice = gst_element_factory_make ("nicesink", NULL);
2939       if (!nice) {
2940         /* FIXME: is this the right thing for a missing plugin? */
2941         GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, (NULL),
2942             ("%s", "libnice elements are not available"));
2943         return GST_STATE_CHANGE_FAILURE;
2944       }
2945       gst_object_unref (nice);
2946       _update_need_negotiation (webrtc);
2947       break;
2948     }
2949     case GST_STATE_CHANGE_READY_TO_PAUSED:
2950       webrtc->priv->running = TRUE;
2951       break;
2952     default:
2953       break;
2954   }
2955
2956   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2957   if (ret == GST_STATE_CHANGE_FAILURE)
2958     return ret;
2959
2960   switch (transition) {
2961     case GST_STATE_CHANGE_READY_TO_PAUSED:
2962       /* Mangle the return value to NO_PREROLL as that's what really is
2963        * occurring here however cannot be propagated correctly due to nicesrc
2964        * requiring that it be in PLAYING already in order to send/receive
2965        * correctly :/ */
2966       ret = GST_STATE_CHANGE_NO_PREROLL;
2967       break;
2968     case GST_STATE_CHANGE_PAUSED_TO_READY:
2969       webrtc->priv->running = FALSE;
2970       break;
2971     default:
2972       break;
2973   }
2974
2975   return ret;
2976 }
2977
2978 static GstPad *
2979 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
2980     const gchar * name, const GstCaps * caps)
2981 {
2982   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
2983   GstWebRTCBinPad *pad = NULL;
2984   GstPluginFeature *feature;
2985   guint serial;
2986
2987   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
2988   if (feature) {
2989     gst_object_unref (feature);
2990   } else {
2991     GST_ELEMENT_ERROR (element, CORE, MISSING_PLUGIN, NULL,
2992         ("%s", "libnice elements are not available"));
2993     return NULL;
2994   }
2995
2996   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
2997   if (feature) {
2998     gst_object_unref (feature);
2999   } else {
3000     GST_ELEMENT_ERROR (element, CORE, MISSING_PLUGIN, NULL,
3001         ("%s", "libnice elements are not available"));
3002     return NULL;
3003   }
3004
3005   if (templ->direction == GST_PAD_SINK ||
3006       g_strcmp0 (templ->name_template, "sink_%u") == 0) {
3007     GstWebRTCRTPTransceiver *trans;
3008
3009     GST_OBJECT_LOCK (webrtc);
3010     if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
3011       /* no name given when requesting the pad, use next available int */
3012       serial = webrtc->priv->max_sink_pad_serial++;
3013     } else {
3014       /* parse serial number from requested padname */
3015       serial = g_ascii_strtoull (&name[5], NULL, 10);
3016       if (serial > webrtc->priv->max_sink_pad_serial)
3017         webrtc->priv->max_sink_pad_serial = serial;
3018     }
3019     GST_OBJECT_UNLOCK (webrtc);
3020
3021     pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial);
3022     trans = _find_transceiver_for_mline (webrtc, serial);
3023     if (!(trans =
3024             GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc)))) {
3025       trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
3026       trans->mline = serial;
3027     }
3028     pad->trans = gst_object_ref (trans);
3029     _connect_input_stream (webrtc, pad);
3030
3031     /* TODO: update negotiation-needed */
3032     _add_pad (webrtc, pad);
3033   }
3034
3035   return GST_PAD (pad);
3036 }
3037
3038 static void
3039 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
3040 {
3041   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
3042   GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
3043
3044   if (webrtc_pad->trans)
3045     gst_object_unref (webrtc_pad->trans);
3046   webrtc_pad->trans = NULL;
3047
3048   _remove_pad (webrtc, webrtc_pad);
3049 }
3050
3051 static void
3052 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
3053     const GValue * value, GParamSpec * pspec)
3054 {
3055   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3056
3057   switch (prop_id) {
3058     case PROP_STUN_SERVER:
3059     case PROP_TURN_SERVER:
3060       g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
3061       break;
3062     default:
3063       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3064       break;
3065   }
3066 }
3067
3068 static void
3069 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
3070     GValue * value, GParamSpec * pspec)
3071 {
3072   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3073
3074   PC_LOCK (webrtc);
3075   switch (prop_id) {
3076     case PROP_CONNECTION_STATE:
3077       g_value_set_enum (value, webrtc->peer_connection_state);
3078       break;
3079     case PROP_SIGNALING_STATE:
3080       g_value_set_enum (value, webrtc->signaling_state);
3081       break;
3082     case PROP_ICE_GATHERING_STATE:
3083       g_value_set_enum (value, webrtc->ice_gathering_state);
3084       break;
3085     case PROP_ICE_CONNECTION_STATE:
3086       g_value_set_enum (value, webrtc->ice_connection_state);
3087       break;
3088     case PROP_LOCAL_DESCRIPTION:
3089       if (webrtc->pending_local_description)
3090         g_value_set_boxed (value, webrtc->pending_local_description);
3091       else if (webrtc->current_local_description)
3092         g_value_set_boxed (value, webrtc->current_local_description);
3093       else
3094         g_value_set_boxed (value, NULL);
3095       break;
3096     case PROP_CURRENT_LOCAL_DESCRIPTION:
3097       g_value_set_boxed (value, webrtc->current_local_description);
3098       break;
3099     case PROP_PENDING_LOCAL_DESCRIPTION:
3100       g_value_set_boxed (value, webrtc->pending_local_description);
3101       break;
3102     case PROP_REMOTE_DESCRIPTION:
3103       if (webrtc->pending_remote_description)
3104         g_value_set_boxed (value, webrtc->pending_remote_description);
3105       else if (webrtc->current_remote_description)
3106         g_value_set_boxed (value, webrtc->current_remote_description);
3107       else
3108         g_value_set_boxed (value, NULL);
3109       break;
3110     case PROP_CURRENT_REMOTE_DESCRIPTION:
3111       g_value_set_boxed (value, webrtc->current_remote_description);
3112       break;
3113     case PROP_PENDING_REMOTE_DESCRIPTION:
3114       g_value_set_boxed (value, webrtc->pending_remote_description);
3115       break;
3116     case PROP_STUN_SERVER:
3117     case PROP_TURN_SERVER:
3118       g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
3119       break;
3120     default:
3121       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3122       break;
3123   }
3124   PC_UNLOCK (webrtc);
3125 }
3126
3127 static void
3128 _free_pending_pad (GstPad * pad)
3129 {
3130   gst_object_unref (pad);
3131 }
3132
3133 static void
3134 gst_webrtc_bin_dispose (GObject * object)
3135 {
3136   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3137
3138   _stop_thread (webrtc);
3139
3140   if (webrtc->priv->ice)
3141     gst_object_unref (webrtc->priv->ice);
3142   webrtc->priv->ice = NULL;
3143
3144   if (webrtc->priv->ice_stream_map)
3145     g_array_free (webrtc->priv->ice_stream_map, TRUE);
3146   webrtc->priv->ice_stream_map = NULL;
3147
3148   G_OBJECT_CLASS (parent_class)->dispose (object);
3149 }
3150
3151 static void
3152 gst_webrtc_bin_finalize (GObject * object)
3153 {
3154   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3155
3156   if (webrtc->priv->transports)
3157     g_array_free (webrtc->priv->transports, TRUE);
3158   webrtc->priv->transports = NULL;
3159
3160   if (webrtc->priv->transceivers)
3161     g_array_free (webrtc->priv->transceivers, TRUE);
3162   webrtc->priv->transceivers = NULL;
3163
3164   if (webrtc->priv->pending_ice_candidates)
3165     g_array_free (webrtc->priv->pending_ice_candidates, TRUE);
3166   webrtc->priv->pending_ice_candidates = NULL;
3167
3168   if (webrtc->priv->session_mid_map)
3169     g_array_free (webrtc->priv->session_mid_map, TRUE);
3170   webrtc->priv->session_mid_map = NULL;
3171
3172   if (webrtc->priv->pending_pads)
3173     g_list_free_full (webrtc->priv->pending_pads,
3174         (GDestroyNotify) _free_pending_pad);
3175   webrtc->priv->pending_pads = NULL;
3176
3177   if (webrtc->current_local_description)
3178     gst_webrtc_session_description_free (webrtc->current_local_description);
3179   webrtc->current_local_description = NULL;
3180   if (webrtc->pending_local_description)
3181     gst_webrtc_session_description_free (webrtc->pending_local_description);
3182   webrtc->pending_local_description = NULL;
3183
3184   if (webrtc->current_remote_description)
3185     gst_webrtc_session_description_free (webrtc->current_remote_description);
3186   webrtc->current_remote_description = NULL;
3187   if (webrtc->pending_remote_description)
3188     gst_webrtc_session_description_free (webrtc->pending_remote_description);
3189   webrtc->pending_remote_description = NULL;
3190
3191   if (webrtc->priv->stats)
3192     gst_structure_free (webrtc->priv->stats);
3193   webrtc->priv->stats = NULL;
3194
3195   G_OBJECT_CLASS (parent_class)->finalize (object);
3196 }
3197
3198 static void
3199 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
3200 {
3201   GObjectClass *gobject_class = (GObjectClass *) klass;
3202   GstElementClass *element_class = (GstElementClass *) klass;
3203
3204   g_type_class_add_private (klass, sizeof (GstWebRTCBinPrivate));
3205
3206   element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
3207   element_class->release_pad = gst_webrtc_bin_release_pad;
3208   element_class->change_state = gst_webrtc_bin_change_state;
3209
3210   gst_element_class_add_static_pad_template (element_class, &sink_template);
3211   gst_element_class_add_static_pad_template (element_class, &src_template);
3212
3213   gst_element_class_set_metadata (element_class, "WebRTC Bin",
3214       "Filter/Network/WebRTC", "A bin for webrtc connections",
3215       "Matthew Waters <matthew@centricular.com>");
3216
3217   gobject_class->get_property = gst_webrtc_bin_get_property;
3218   gobject_class->set_property = gst_webrtc_bin_set_property;
3219   gobject_class->dispose = gst_webrtc_bin_dispose;
3220   gobject_class->finalize = gst_webrtc_bin_finalize;
3221
3222   g_object_class_install_property (gobject_class,
3223       PROP_LOCAL_DESCRIPTION,
3224       g_param_spec_boxed ("local-description", "Local Description",
3225           "The local SDP description to use for this connection",
3226           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
3227           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3228
3229   g_object_class_install_property (gobject_class,
3230       PROP_REMOTE_DESCRIPTION,
3231       g_param_spec_boxed ("remote-description", "Remote Description",
3232           "The remote SDP description to use for this connection",
3233           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
3234           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3235
3236   g_object_class_install_property (gobject_class,
3237       PROP_STUN_SERVER,
3238       g_param_spec_string ("stun-server", "STUN Server",
3239           "The STUN server of the form stun://hostname:port",
3240           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3241
3242   g_object_class_install_property (gobject_class,
3243       PROP_TURN_SERVER,
3244       g_param_spec_string ("turn-server", "TURN Server",
3245           "The TURN server of the form turn(s)://username:password@host:port",
3246           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3247
3248   g_object_class_install_property (gobject_class,
3249       PROP_CONNECTION_STATE,
3250       g_param_spec_enum ("connection-state", "Connection State",
3251           "The overall connection state of this element",
3252           GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
3253           GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
3254           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3255
3256   g_object_class_install_property (gobject_class,
3257       PROP_SIGNALING_STATE,
3258       g_param_spec_enum ("signaling-state", "Signaling State",
3259           "The signaling state of this element",
3260           GST_TYPE_WEBRTC_SIGNALING_STATE,
3261           GST_WEBRTC_SIGNALING_STATE_STABLE,
3262           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3263
3264   g_object_class_install_property (gobject_class,
3265       PROP_ICE_CONNECTION_STATE,
3266       g_param_spec_enum ("ice-connection-state", "ICE connection state",
3267           "The collective connection state of all ICETransport's",
3268           GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
3269           GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
3270           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3271
3272   g_object_class_install_property (gobject_class,
3273       PROP_ICE_GATHERING_STATE,
3274       g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
3275           "The collective gathering state of all ICETransport's",
3276           GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
3277           GST_WEBRTC_ICE_GATHERING_STATE_NEW,
3278           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3279
3280   /**
3281    * GstWebRTCBin::create-offer:
3282    * @object: the #GstWebRtcBin
3283    * @options: create-offer options
3284    * @promise: a #GstPromise which will contain the offer
3285    */
3286   gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
3287       g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
3288       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3289       G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL,
3290       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
3291       GST_TYPE_PROMISE);
3292
3293   /**
3294    * GstWebRTCBin::create-answer:
3295    * @object: the #GstWebRtcBin
3296    * @options: create-answer options
3297    * @promise: a #GstPromise which will contain the answer
3298    */
3299   gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
3300       g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
3301       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3302       G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL,
3303       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
3304       GST_TYPE_PROMISE);
3305
3306   /**
3307    * GstWebRTCBin::set-local-description:
3308    * @object: the #GstWebRtcBin
3309    * @type: the type of description being set
3310    * @sdp: a #GstSDPMessage description
3311    * @promise (allow-none): a #GstPromise to be notified when it's set
3312    */
3313   gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
3314       g_signal_new_class_handler ("set-local-description",
3315       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3316       G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL,
3317       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
3318       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
3319
3320   /**
3321    * GstWebRTCBin::set-remote-description:
3322    * @object: the #GstWebRtcBin
3323    * @type: the type of description being set
3324    * @sdp: a #GstSDPMessage description
3325    * @promise (allow-none): a #GstPromise to be notified when it's set
3326    */
3327   gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
3328       g_signal_new_class_handler ("set-remote-description",
3329       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3330       G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL,
3331       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
3332       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
3333
3334   /**
3335    * GstWebRTCBin::add-ice-candidate:
3336    * @object: the #GstWebRtcBin
3337    * @ice-candidate: an ice candidate
3338    */
3339   gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
3340       g_signal_new_class_handler ("add-ice-candidate",
3341       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3342       G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL,
3343       g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
3344
3345   /**
3346    * GstWebRTCBin::get-stats:
3347    * @object: the #GstWebRtcBin
3348    * @promise: a #GstPromise for the result
3349    *
3350    * The @promise will contain the result of retrieving the session statistics.
3351    * The structure will be named 'application/x-webrtc-stats and contain the
3352    * following based on the webrtc-stats spec available from
3353    * https://www.w3.org/TR/webrtc-stats/.  As the webrtc-stats spec is a draft
3354    * and is constantly changing these statistics may be changed to fit with
3355    * the latest spec.
3356    *
3357    * Each field key is a unique identifer for each RTCStats
3358    * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
3359    * GstStructure) in the RTCStatsReport
3360    * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object).  Each supported
3361    * field in the RTCStats subclass is outlined below.
3362    *
3363    * Each statistics structure contains the following values as defined by
3364    * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
3365    *
3366    *  "timestamp"           G_TYPE_DOUBLE               timestamp the statistics were generated
3367    *  "type"                GST_TYPE_WEBRTC_STATS_TYPE  the type of statistics reported
3368    *  "id"                  G_TYPE_STRING               unique identifier
3369    *
3370    * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
3371    *
3372    *  "payload-type"        G_TYPE_UINT                 the rtp payload number in use
3373    *  "clock-rate"          G_TYPE_UINT                 the rtp clock-rate
3374    *
3375    * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
3376    *
3377    *  "ssrc"                G_TYPE_STRING               the rtp sequence src in use
3378    *  "transport-id"        G_TYPE_STRING               identifier for the associated RTCTransportStats for this stream
3379    *  "codec-id"            G_TYPE_STRING               identifier for the associated RTCCodecStats for this stream
3380    *  "fir-count"           G_TYPE_UINT                 FIR requests received by the sender (only for local statistics)
3381    *  "pli-count"           G_TYPE_UINT                 PLI requests received by the sender (only for local statistics)
3382    *  "nack-count"          G_TYPE_UINT                 NACK requests received by the sender (only for local statistics)
3383    *
3384    * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
3385    *
3386    *  "packets-received"     G_TYPE_UINT64              number of packets received (only for local inbound)
3387    *  "bytes-received"       G_TYPE_UINT64              number of bytes received (only for local inbound)
3388    *  "packets-lost"         G_TYPE_UINT                number of packets lost
3389    *  "jitter"               G_TYPE_DOUBLE              packet jitter measured in secondss
3390    *
3391    * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
3392    *
3393    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteOutboundRTPSTreamStats
3394    *
3395    * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
3396    *
3397    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCOutboundRTPSTreamStats
3398    *  "round-trip-time"     G_TYPE_DOUBLE               round trip time of packets measured in seconds
3399    *
3400    * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
3401    *
3402    *  "packets-sent"        G_TYPE_UINT64               number of packets sent (only for local outbound)
3403    *  "bytes-sent"          G_TYPE_UINT64               number of packets sent (only for local outbound)
3404    *
3405    * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
3406    *
3407    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteInboundRTPSTreamStats
3408    *
3409    * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
3410    *
3411    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCInboundRTPSTreamStats
3412    *
3413    */
3414   gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
3415       g_signal_new_class_handler ("get-stats",
3416       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3417       G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL,
3418       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_PAD,
3419       GST_TYPE_PROMISE);
3420
3421   /**
3422    * GstWebRTCBin::on-negotiation-needed:
3423    * @object: the #GstWebRtcBin
3424    */
3425   gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
3426       g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
3427       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
3428       G_TYPE_NONE, 0);
3429
3430   /**
3431    * GstWebRTCBin::on-ice-candidate:
3432    * @object: the #GstWebRtcBin
3433    * @candidate: the ICE candidate
3434    */
3435   gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
3436       g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
3437       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
3438       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
3439
3440   /**
3441    * GstWebRTCBin::add-transceiver:
3442    * @object: the #GstWebRtcBin
3443    * @direction: the direction of the new transceiver
3444    * @caps: (allow none): the codec preferences for this transceiver
3445    *
3446    * Returns: the new #GstWebRTCRTPTransceiver
3447    */
3448   gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
3449       g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
3450       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3451       G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
3452       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
3453       GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
3454
3455   /**
3456    * GstWebRTCBin::get-transceivers:
3457    * @object: the #GstWebRtcBin
3458    *
3459    * Returns: a #GArray of #GstWebRTCRTPTransceivers
3460    */
3461   gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
3462       g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
3463       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3464       G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL,
3465       g_cclosure_marshal_generic, G_TYPE_ARRAY, 0);
3466 }
3467
3468 static void
3469 _deref_unparent_and_unref (GObject ** object)
3470 {
3471   GstObject *obj = GST_OBJECT (*object);
3472
3473   GST_OBJECT_PARENT (obj) = NULL;
3474
3475   gst_object_unref (*object);
3476 }
3477
3478 static void
3479 _transport_free (GObject ** object)
3480 {
3481   TransportStream *stream = (TransportStream *) * object;
3482   GstWebRTCBin *webrtc;
3483
3484   webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
3485
3486   if (stream->transport) {
3487     g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
3488     g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
3489   }
3490   if (stream->rtcp_transport) {
3491     g_signal_handlers_disconnect_by_data (stream->rtcp_transport->transport,
3492         webrtc);
3493     g_signal_handlers_disconnect_by_data (stream->rtcp_transport, webrtc);
3494   }
3495
3496   gst_object_unref (*object);
3497 }
3498
3499 static void
3500 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
3501 {
3502   webrtc->priv =
3503       G_TYPE_INSTANCE_GET_PRIVATE ((webrtc), GST_TYPE_WEBRTC_BIN,
3504       GstWebRTCBinPrivate);
3505
3506   _start_thread (webrtc);
3507
3508   webrtc->rtpbin = _create_rtpbin (webrtc);
3509   gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
3510
3511   webrtc->priv->transceivers = g_array_new (FALSE, TRUE, sizeof (gpointer));
3512   g_array_set_clear_func (webrtc->priv->transceivers,
3513       (GDestroyNotify) _deref_unparent_and_unref);
3514
3515   webrtc->priv->transports = g_array_new (FALSE, TRUE, sizeof (gpointer));
3516   g_array_set_clear_func (webrtc->priv->transports,
3517       (GDestroyNotify) _transport_free);
3518
3519   webrtc->priv->session_mid_map =
3520       g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
3521   g_array_set_clear_func (webrtc->priv->session_mid_map,
3522       (GDestroyNotify) clear_session_mid_item);
3523
3524   webrtc->priv->ice = gst_webrtc_ice_new ();
3525   g_signal_connect (webrtc->priv->ice, "on-ice-candidate",
3526       G_CALLBACK (_on_ice_candidate), webrtc);
3527   webrtc->priv->ice_stream_map =
3528       g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
3529   webrtc->priv->pending_ice_candidates =
3530       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem *));
3531   g_array_set_clear_func (webrtc->priv->pending_ice_candidates,
3532       (GDestroyNotify) _clear_ice_candidate_item);
3533 }