1 /* GStreamer
2  * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
28 #include "utils.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
31 #include "webrtcdatachannel.h"
32 #include "sctptransport.h"
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #define RANDOM_SESSION_ID \
39     ((((((guint64) g_random_int()) << 32) | \
40        (guint64) g_random_int ())) & \
41     G_GUINT64_CONSTANT (0x7fffffffffffffff))
42 
43 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
44 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
45 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
46 
47 #define PC_GET_COND(w) (&w->priv->pc_cond)
48 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
49 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
50 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
51 
52 /*
53  * This webrtcbin implements the majority of the W3's peerconnection API and
54  * implementation guide where possible. Generating offers, answers and setting
55  * local and remote SDP's are all supported.  Both media descriptions and
56  * descriptions involving data channels are supported.
57  *
58  * Each input/output pad is equivalent to a Track in W3 parlance which are
59  * added/removed from the bin.  The number of requested sink pads is the number
60  * of streams that will be sent to the receiver and will be associated with a
61  * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
62  *
63  * On the receiving side, RTPTransceiver's are created in response to setting
64  * a remote description.  Output pads for the receiving streams in the set
65  * description are also created when data is received.
66  *
67  * A TransportStream is created when needed in order to transport the data over
68  * the necessary DTLS/ICE channel to the peer.  The exact configuration depends
69  * on the negotiated SDP's between the peers based on the bundle and rtcp
70  * configuration.  Some cases are outlined below for a simple single
71  * audio/video/data session:
72  *
73  * - max-bundle (requires rtcp-muxing) uses a single transport for all
74  *   media/data transported.  Renegotiation involves adding/removing the
75  *   necessary streams to the existing transports.
76  * - max-compat without rtcp-mux involves two TransportStream per media stream
77  *   to transport the rtp and the rtcp packets and a single TransportStream for
78  *   all data channels.  Each stream change involves modifying the associated
79  *   TransportStream/s as necessary.
80  */
81 
82 /*
83  * TODO:
84  * assert sending payload type matches the stream
85  * reconfiguration (of anything)
86  * LS groups
87  * balanced bundle policy
88  * setting custom DTLS certificates
89  *
90  * seperate session id's from mlineindex properly
91  * how to deal with replacing a input/output track/stream
92  */
93 
94 static void _update_need_negotiation (GstWebRTCBin * webrtc);
95 
96 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
97 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
98 
99 static gboolean
_have_nice_elements(GstWebRTCBin * webrtc)100 _have_nice_elements (GstWebRTCBin * webrtc)
101 {
102   GstPluginFeature *feature;
103 
104   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
105   if (feature) {
106     gst_object_unref (feature);
107   } else {
108     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
109         ("%s", "libnice elements are not available"));
110     return FALSE;
111   }
112 
113   feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
114   if (feature) {
115     gst_object_unref (feature);
116   } else {
117     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
118         ("%s", "libnice elements are not available"));
119     return FALSE;
120   }
121 
122   return TRUE;
123 }
124 
125 static gboolean
_have_sctp_elements(GstWebRTCBin * webrtc)126 _have_sctp_elements (GstWebRTCBin * webrtc)
127 {
128   GstPluginFeature *feature;
129 
130   feature = gst_registry_lookup_feature (gst_registry_get (), "sctpdec");
131   if (feature) {
132     gst_object_unref (feature);
133   } else {
134     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
135         ("%s", "sctp elements are not available"));
136     return FALSE;
137   }
138 
139   feature = gst_registry_lookup_feature (gst_registry_get (), "sctpenc");
140   if (feature) {
141     gst_object_unref (feature);
142   } else {
143     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
144         ("%s", "sctp elements are not available"));
145     return FALSE;
146   }
147 
148   return TRUE;
149 }
150 
151 static gboolean
_have_dtls_elements(GstWebRTCBin * webrtc)152 _have_dtls_elements (GstWebRTCBin * webrtc)
153 {
154   GstPluginFeature *feature;
155 
156   feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsdec");
157   if (feature) {
158     gst_object_unref (feature);
159   } else {
160     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
161         ("%s", "dtls elements are not available"));
162     return FALSE;
163   }
164 
165   feature = gst_registry_lookup_feature (gst_registry_get (), "dtlsenc");
166   if (feature) {
167     gst_object_unref (feature);
168   } else {
169     GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, NULL,
170         ("%s", "dtls elements are not available"));
171     return FALSE;
172   }
173 
174   return TRUE;
175 }
176 
177 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
178 
179 static void
gst_webrtc_bin_pad_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)180 gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
181     const GValue * value, GParamSpec * pspec)
182 {
183   switch (prop_id) {
184     default:
185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186       break;
187   }
188 }
189 
190 static void
gst_webrtc_bin_pad_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)191 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
192     GValue * value, GParamSpec * pspec)
193 {
194   switch (prop_id) {
195     default:
196       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
197       break;
198   }
199 }
200 
201 static void
gst_webrtc_bin_pad_finalize(GObject * object)202 gst_webrtc_bin_pad_finalize (GObject * object)
203 {
204   GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
205 
206   if (pad->trans)
207     gst_object_unref (pad->trans);
208   pad->trans = NULL;
209 
210   if (pad->received_caps)
211     gst_caps_unref (pad->received_caps);
212   pad->received_caps = NULL;
213 
214   G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
215 }
216 
217 static void
gst_webrtc_bin_pad_class_init(GstWebRTCBinPadClass * klass)218 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
219 {
220   GObjectClass *gobject_class = (GObjectClass *) klass;
221 
222   gobject_class->get_property = gst_webrtc_bin_pad_get_property;
223   gobject_class->set_property = gst_webrtc_bin_pad_set_property;
224   gobject_class->finalize = gst_webrtc_bin_pad_finalize;
225 }
226 
227 static gboolean
gst_webrtcbin_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)228 gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
229 {
230   GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
231 
232   if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
233     GstCaps *caps;
234     gboolean do_update;
235 
236     gst_event_parse_caps (event, &caps);
237     do_update = (!wpad->received_caps
238         || gst_caps_is_equal (wpad->received_caps, caps));
239     gst_caps_replace (&wpad->received_caps, caps);
240 
241     if (do_update)
242       _update_need_negotiation (GST_WEBRTC_BIN (parent));
243   }
244 
245   return gst_pad_event_default (pad, parent, event);
246 }
247 
248 static void
gst_webrtc_bin_pad_init(GstWebRTCBinPad * pad)249 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
250 {
251 }
252 
253 static GstWebRTCBinPad *
gst_webrtc_bin_pad_new(const gchar * name,GstPadDirection direction)254 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
255 {
256   GstWebRTCBinPad *pad =
257       g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
258       direction, NULL);
259 
260   gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event);
261 
262   if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
263     gst_object_unref (pad);
264     return NULL;
265   }
266 
267   GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
268       direction == GST_PAD_SRC ? "src" : "sink");
269   return pad;
270 }
271 
272 #define gst_webrtc_bin_parent_class parent_class
273 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
274     G_ADD_PRIVATE (GstWebRTCBin)
275     GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
276         "webrtcbin element");
277     );
278 
279 static GstPad *_connect_input_stream (GstWebRTCBin * webrtc,
280     GstWebRTCBinPad * pad);
281 
282 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
283     GST_PAD_SINK,
284     GST_PAD_REQUEST,
285     GST_STATIC_CAPS ("application/x-rtp"));
286 
287 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
288     GST_PAD_SRC,
289     GST_PAD_SOMETIMES,
290     GST_STATIC_CAPS ("application/x-rtp"));
291 
292 enum
293 {
294   SIGNAL_0,
295   CREATE_OFFER_SIGNAL,
296   CREATE_ANSWER_SIGNAL,
297   SET_LOCAL_DESCRIPTION_SIGNAL,
298   SET_REMOTE_DESCRIPTION_SIGNAL,
299   ADD_ICE_CANDIDATE_SIGNAL,
300   ON_NEGOTIATION_NEEDED_SIGNAL,
301   ON_ICE_CANDIDATE_SIGNAL,
302   ON_NEW_TRANSCEIVER_SIGNAL,
303   GET_STATS_SIGNAL,
304   ADD_TRANSCEIVER_SIGNAL,
305   GET_TRANSCEIVER_SIGNAL,
306   GET_TRANSCEIVERS_SIGNAL,
307   ADD_TURN_SERVER_SIGNAL,
308   CREATE_DATA_CHANNEL_SIGNAL,
309   ON_DATA_CHANNEL_SIGNAL,
310   LAST_SIGNAL,
311 };
312 
313 enum
314 {
315   PROP_0,
316   PROP_CONNECTION_STATE,
317   PROP_SIGNALING_STATE,
318   PROP_ICE_GATHERING_STATE,
319   PROP_ICE_CONNECTION_STATE,
320   PROP_LOCAL_DESCRIPTION,
321   PROP_CURRENT_LOCAL_DESCRIPTION,
322   PROP_PENDING_LOCAL_DESCRIPTION,
323   PROP_REMOTE_DESCRIPTION,
324   PROP_CURRENT_REMOTE_DESCRIPTION,
325   PROP_PENDING_REMOTE_DESCRIPTION,
326   PROP_STUN_SERVER,
327   PROP_TURN_SERVER,
328   PROP_BUNDLE_POLICY,
329   PROP_ICE_TRANSPORT_POLICY,
330 };
331 
332 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
333 
334 typedef struct
335 {
336   guint session_id;
337   GstWebRTCICEStream *stream;
338 } IceStreamItem;
339 
340 /* FIXME: locking? */
341 GstWebRTCICEStream *
_find_ice_stream_for_session(GstWebRTCBin * webrtc,guint session_id)342 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
343 {
344   int i;
345 
346   for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
347     IceStreamItem *item =
348         &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
349 
350     if (item->session_id == session_id) {
351       GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
352           "session %u", item->stream, session_id);
353       return item->stream;
354     }
355   }
356 
357   GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
358       session_id);
359   return NULL;
360 }
361 
362 void
_add_ice_stream_item(GstWebRTCBin * webrtc,guint session_id,GstWebRTCICEStream * stream)363 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
364     GstWebRTCICEStream * stream)
365 {
366   IceStreamItem item = { session_id, stream };
367 
368   GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
369       "session %u", stream, session_id);
370   g_array_append_val (webrtc->priv->ice_stream_map, item);
371 }
372 
373 typedef struct
374 {
375   guint session_id;
376   gchar *mid;
377 } SessionMidItem;
378 
379 static void
clear_session_mid_item(SessionMidItem * item)380 clear_session_mid_item (SessionMidItem * item)
381 {
382   g_free (item->mid);
383 }
384 
385 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
386     gconstpointer data);
387 
388 static GstWebRTCRTPTransceiver *
_find_transceiver(GstWebRTCBin * webrtc,gconstpointer data,FindTransceiverFunc func)389 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
390     FindTransceiverFunc func)
391 {
392   int i;
393 
394   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
395     GstWebRTCRTPTransceiver *transceiver =
396         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
397         i);
398 
399     if (func (transceiver, data))
400       return transceiver;
401   }
402 
403   return NULL;
404 }
405 
406 static gboolean
match_for_mid(GstWebRTCRTPTransceiver * trans,const gchar * mid)407 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
408 {
409   return g_strcmp0 (trans->mid, mid) == 0;
410 }
411 
412 static gboolean
transceiver_match_for_mline(GstWebRTCRTPTransceiver * trans,guint * mline)413 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
414 {
415   return trans->mline == *mline;
416 }
417 
418 static GstWebRTCRTPTransceiver *
_find_transceiver_for_mline(GstWebRTCBin * webrtc,guint mlineindex)419 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
420 {
421   GstWebRTCRTPTransceiver *trans;
422 
423   trans = _find_transceiver (webrtc, &mlineindex,
424       (FindTransceiverFunc) transceiver_match_for_mline);
425 
426   GST_TRACE_OBJECT (webrtc,
427       "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
428       mlineindex);
429 
430   return trans;
431 }
432 
433 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
434     gconstpointer data);
435 
436 static TransportStream *
_find_transport(GstWebRTCBin * webrtc,gconstpointer data,FindTransportFunc func)437 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
438     FindTransportFunc func)
439 {
440   int i;
441 
442   for (i = 0; i < webrtc->priv->transports->len; i++) {
443     TransportStream *stream =
444         g_array_index (webrtc->priv->transports, TransportStream *,
445         i);
446 
447     if (func (stream, data))
448       return stream;
449   }
450 
451   return NULL;
452 }
453 
454 static gboolean
match_stream_for_session(TransportStream * trans,guint * session)455 match_stream_for_session (TransportStream * trans, guint * session)
456 {
457   return trans->session_id == *session;
458 }
459 
460 static TransportStream *
_find_transport_for_session(GstWebRTCBin * webrtc,guint session_id)461 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
462 {
463   TransportStream *stream;
464 
465   stream = _find_transport (webrtc, &session_id,
466       (FindTransportFunc) match_stream_for_session);
467 
468   GST_TRACE_OBJECT (webrtc,
469       "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
470 
471   return stream;
472 }
473 
474 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
475 
476 static GstWebRTCBinPad *
_find_pad(GstWebRTCBin * webrtc,gconstpointer data,FindPadFunc func)477 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
478 {
479   GstElement *element = GST_ELEMENT (webrtc);
480   GList *l;
481 
482   GST_OBJECT_LOCK (webrtc);
483   l = element->pads;
484   for (; l; l = g_list_next (l)) {
485     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
486       continue;
487     if (func (l->data, data)) {
488       gst_object_ref (l->data);
489       GST_OBJECT_UNLOCK (webrtc);
490       return l->data;
491     }
492   }
493 
494   l = webrtc->priv->pending_pads;
495   for (; l; l = g_list_next (l)) {
496     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
497       continue;
498     if (func (l->data, data)) {
499       gst_object_ref (l->data);
500       GST_OBJECT_UNLOCK (webrtc);
501       return l->data;
502     }
503   }
504   GST_OBJECT_UNLOCK (webrtc);
505 
506   return NULL;
507 }
508 
509 typedef gboolean (*FindDataChannelFunc) (GstWebRTCDataChannel * p1,
510     gconstpointer data);
511 
512 static GstWebRTCDataChannel *
_find_data_channel(GstWebRTCBin * webrtc,gconstpointer data,FindDataChannelFunc func)513 _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data,
514     FindDataChannelFunc func)
515 {
516   int i;
517 
518   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
519     GstWebRTCDataChannel *channel =
520         g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
521         i);
522 
523     if (func (channel, data))
524       return channel;
525   }
526 
527   return NULL;
528 }
529 
530 static gboolean
data_channel_match_for_id(GstWebRTCDataChannel * channel,gint * id)531 data_channel_match_for_id (GstWebRTCDataChannel * channel, gint * id)
532 {
533   return channel->id == *id;
534 }
535 
536 static GstWebRTCDataChannel *
_find_data_channel_for_id(GstWebRTCBin * webrtc,gint id)537 _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id)
538 {
539   GstWebRTCDataChannel *channel;
540 
541   channel = _find_data_channel (webrtc, &id,
542       (FindDataChannelFunc) data_channel_match_for_id);
543 
544   GST_TRACE_OBJECT (webrtc,
545       "Found data channel %" GST_PTR_FORMAT " for id %i", channel, id);
546 
547   return channel;
548 }
549 
550 static void
_add_pad_to_list(GstWebRTCBin * webrtc,GstWebRTCBinPad * pad)551 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
552 {
553   GST_OBJECT_LOCK (webrtc);
554   webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
555   GST_OBJECT_UNLOCK (webrtc);
556 }
557 
558 static void
_remove_pending_pad(GstWebRTCBin * webrtc,GstWebRTCBinPad * pad)559 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
560 {
561   GST_OBJECT_LOCK (webrtc);
562   webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
563   GST_OBJECT_UNLOCK (webrtc);
564 }
565 
566 static void
_add_pad(GstWebRTCBin * webrtc,GstWebRTCBinPad * pad)567 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
568 {
569   _remove_pending_pad (webrtc, pad);
570 
571   if (webrtc->priv->running)
572     gst_pad_set_active (GST_PAD (pad), TRUE);
573   gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
574 }
575 
576 static void
_remove_pad(GstWebRTCBin * webrtc,GstWebRTCBinPad * pad)577 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
578 {
579   _remove_pending_pad (webrtc, pad);
580 
581   gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
582 }
583 
584 typedef struct
585 {
586   GstPadDirection direction;
587   guint mlineindex;
588 } MLineMatch;
589 
590 static gboolean
pad_match_for_mline(GstWebRTCBinPad * pad,const MLineMatch * match)591 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
592 {
593   return GST_PAD_DIRECTION (pad) == match->direction
594       && pad->mlineindex == match->mlineindex;
595 }
596 
597 static GstWebRTCBinPad *
_find_pad_for_mline(GstWebRTCBin * webrtc,GstPadDirection direction,guint mlineindex)598 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
599     guint mlineindex)
600 {
601   MLineMatch m = { direction, mlineindex };
602 
603   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
604 }
605 
606 typedef struct
607 {
608   GstPadDirection direction;
609   GstWebRTCRTPTransceiver *trans;
610 } TransMatch;
611 
612 static gboolean
pad_match_for_transceiver(GstWebRTCBinPad * pad,TransMatch * m)613 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
614 {
615   return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
616 }
617 
618 static GstWebRTCBinPad *
_find_pad_for_transceiver(GstWebRTCBin * webrtc,GstPadDirection direction,GstWebRTCRTPTransceiver * trans)619 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
620     GstWebRTCRTPTransceiver * trans)
621 {
622   TransMatch m = { direction, trans };
623 
624   return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
625 }
626 
627 #if 0
628 static gboolean
629 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
630 {
631   return pad->ssrc == *ssrc;
632 }
633 
634 static gboolean
635 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
636 {
637   return pad == other;
638 }
639 #endif
640 
641 static gboolean
_unlock_pc_thread(GMutex * lock)642 _unlock_pc_thread (GMutex * lock)
643 {
644   g_mutex_unlock (lock);
645   return G_SOURCE_REMOVE;
646 }
647 
648 static gpointer
_gst_pc_thread(GstWebRTCBin * webrtc)649 _gst_pc_thread (GstWebRTCBin * webrtc)
650 {
651   PC_LOCK (webrtc);
652   webrtc->priv->main_context = g_main_context_new ();
653   webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
654 
655   PC_COND_BROADCAST (webrtc);
656   g_main_context_invoke (webrtc->priv->main_context,
657       (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
658 
659   /* Having the thread be the thread default GMainContext will break the
660    * required queue-like ordering (from W3's peerconnection spec) of re-entrant
661    * tasks */
662   g_main_loop_run (webrtc->priv->loop);
663 
664   PC_LOCK (webrtc);
665   g_main_context_unref (webrtc->priv->main_context);
666   webrtc->priv->main_context = NULL;
667   g_main_loop_unref (webrtc->priv->loop);
668   webrtc->priv->loop = NULL;
669   PC_COND_BROADCAST (webrtc);
670   PC_UNLOCK (webrtc);
671 
672   return NULL;
673 }
674 
675 static void
_start_thread(GstWebRTCBin * webrtc)676 _start_thread (GstWebRTCBin * webrtc)
677 {
678   PC_LOCK (webrtc);
679   webrtc->priv->thread = g_thread_new ("gst-pc-ops",
680       (GThreadFunc) _gst_pc_thread, webrtc);
681 
682   while (!webrtc->priv->loop)
683     PC_COND_WAIT (webrtc);
684   webrtc->priv->is_closed = FALSE;
685   PC_UNLOCK (webrtc);
686 }
687 
688 static void
_stop_thread(GstWebRTCBin * webrtc)689 _stop_thread (GstWebRTCBin * webrtc)
690 {
691   PC_LOCK (webrtc);
692   webrtc->priv->is_closed = TRUE;
693   g_main_loop_quit (webrtc->priv->loop);
694   while (webrtc->priv->loop)
695     PC_COND_WAIT (webrtc);
696   PC_UNLOCK (webrtc);
697 
698   g_thread_unref (webrtc->priv->thread);
699 }
700 
701 static gboolean
_execute_op(GstWebRTCBinTask * op)702 _execute_op (GstWebRTCBinTask * op)
703 {
704   PC_LOCK (op->webrtc);
705   if (op->webrtc->priv->is_closed) {
706     GST_DEBUG_OBJECT (op->webrtc,
707         "Peerconnection is closed, aborting execution");
708     goto out;
709   }
710 
711   op->op (op->webrtc, op->data);
712 
713 out:
714   PC_UNLOCK (op->webrtc);
715   return G_SOURCE_REMOVE;
716 }
717 
718 static void
_free_op(GstWebRTCBinTask * op)719 _free_op (GstWebRTCBinTask * op)
720 {
721   if (op->notify)
722     op->notify (op->data);
723   g_free (op);
724 }
725 
726 void
gst_webrtc_bin_enqueue_task(GstWebRTCBin * webrtc,GstWebRTCBinFunc func,gpointer data,GDestroyNotify notify)727 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
728     gpointer data, GDestroyNotify notify)
729 {
730   GstWebRTCBinTask *op;
731   GSource *source;
732 
733   g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc));
734 
735   if (webrtc->priv->is_closed) {
736     GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
737     if (notify)
738       notify (data);
739     return;
740   }
741   op = g_new0 (GstWebRTCBinTask, 1);
742   op->webrtc = webrtc;
743   op->op = func;
744   op->data = data;
745   op->notify = notify;
746 
747   source = g_idle_source_new ();
748   g_source_set_priority (source, G_PRIORITY_DEFAULT);
749   g_source_set_callback (source, (GSourceFunc) _execute_op, op,
750       (GDestroyNotify) _free_op);
751   g_source_attach (source, webrtc->priv->main_context);
752   g_source_unref (source);
753 }
754 
755 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
756 static GstWebRTCICEConnectionState
_collate_ice_connection_states(GstWebRTCBin * webrtc)757 _collate_ice_connection_states (GstWebRTCBin * webrtc)
758 {
759 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
760   GstWebRTCICEConnectionState any_state = 0;
761   gboolean all_closed = TRUE;
762   int i;
763 
764   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
765     GstWebRTCRTPTransceiver *rtp_trans =
766         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
767         i);
768     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
769     TransportStream *stream = trans->stream;
770     GstWebRTCICETransport *transport, *rtcp_transport;
771     GstWebRTCICEConnectionState ice_state;
772     gboolean rtcp_mux = FALSE;
773 
774     if (rtp_trans->stopped)
775       continue;
776     if (!rtp_trans->mid)
777       continue;
778 
779     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
780 
781     transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
782 
783     /* get transport state */
784     g_object_get (transport, "state", &ice_state, NULL);
785     any_state |= (1 << ice_state);
786     if (ice_state != STATE (CLOSED))
787       all_closed = FALSE;
788 
789     rtcp_transport =
790         webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport;
791 
792     if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) {
793       g_object_get (rtcp_transport, "state", &ice_state, NULL);
794       any_state |= (1 << ice_state);
795       if (ice_state != STATE (CLOSED))
796         all_closed = FALSE;
797     }
798   }
799 
800   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
801 
802   if (webrtc->priv->is_closed) {
803     GST_TRACE_OBJECT (webrtc, "returning closed");
804     return STATE (CLOSED);
805   }
806   /* Any of the RTCIceTransport s are in the failed state. */
807   if (any_state & (1 << STATE (FAILED))) {
808     GST_TRACE_OBJECT (webrtc, "returning failed");
809     return STATE (FAILED);
810   }
811   /* Any of the RTCIceTransport s are in the disconnected state and
812    * none of them are in the failed state. */
813   if (any_state & (1 << STATE (DISCONNECTED))) {
814     GST_TRACE_OBJECT (webrtc, "returning disconnected");
815     return STATE (DISCONNECTED);
816   }
817   /* Any of the RTCIceTransport's are in the checking state and none of them
818    * are in the failed or disconnected state. */
819   if (any_state & (1 << STATE (CHECKING))) {
820     GST_TRACE_OBJECT (webrtc, "returning checking");
821     return STATE (CHECKING);
822   }
823   /* Any of the RTCIceTransport s are in the new state and none of them are
824    * in the checking, failed or disconnected state, or all RTCIceTransport's
825    * are in the closed state. */
826   if ((any_state & (1 << STATE (NEW))) || all_closed) {
827     GST_TRACE_OBJECT (webrtc, "returning new");
828     return STATE (NEW);
829   }
830   /* All RTCIceTransport s are in the connected, completed or closed state
831    * and at least one of them is in the connected state. */
832   if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 <<
833           STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) {
834     GST_TRACE_OBJECT (webrtc, "returning connected");
835     return STATE (CONNECTED);
836   }
837   /* All RTCIceTransport s are in the completed or closed state and at least
838    * one of them is in the completed state. */
839   if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED))
840       && any_state & (1 << STATE (COMPLETED))) {
841     GST_TRACE_OBJECT (webrtc, "returning connected");
842     return STATE (CONNECTED);
843   }
844 
845   GST_FIXME ("unspecified situation, returning new");
846   return STATE (NEW);
847 #undef STATE
848 }
849 
850 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
851 static GstWebRTCICEGatheringState
_collate_ice_gathering_states(GstWebRTCBin * webrtc)852 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
853 {
854 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
855   GstWebRTCICEGatheringState any_state = 0;
856   gboolean all_completed = webrtc->priv->transceivers->len > 0;
857   int i;
858 
859   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
860     GstWebRTCRTPTransceiver *rtp_trans =
861         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
862         i);
863     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
864     TransportStream *stream = trans->stream;
865     GstWebRTCICETransport *transport, *rtcp_transport;
866     GstWebRTCICEGatheringState ice_state;
867     gboolean rtcp_mux = FALSE;
868 
869     if (rtp_trans->stopped)
870       continue;
871     if (!rtp_trans->mid)
872       continue;
873 
874     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
875 
876     transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport;
877 
878     /* get gathering state */
879     g_object_get (transport, "gathering-state", &ice_state, NULL);
880     any_state |= (1 << ice_state);
881     if (ice_state != STATE (COMPLETE))
882       all_completed = FALSE;
883 
884     rtcp_transport =
885         webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport;
886 
887     if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
888       g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL);
889       any_state |= (1 << ice_state);
890       if (ice_state != STATE (COMPLETE))
891         all_completed = FALSE;
892     }
893   }
894 
895   GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
896 
897   /* Any of the RTCIceTransport s are in the gathering state. */
898   if (any_state & (1 << STATE (GATHERING))) {
899     GST_TRACE_OBJECT (webrtc, "returning gathering");
900     return STATE (GATHERING);
901   }
902   /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
903    * the completed gathering state. */
904   if (all_completed) {
905     GST_TRACE_OBJECT (webrtc, "returning complete");
906     return STATE (COMPLETE);
907   }
908 
909   /* Any of the RTCIceTransport s are in the new gathering state and none
910    * of the transports are in the gathering state, or there are no transports. */
911   GST_TRACE_OBJECT (webrtc, "returning new");
912   return STATE (NEW);
913 #undef STATE
914 }
915 
916 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
917 static GstWebRTCPeerConnectionState
_collate_peer_connection_states(GstWebRTCBin * webrtc)918 _collate_peer_connection_states (GstWebRTCBin * webrtc)
919 {
920 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
921 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
922 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
923   GstWebRTCICEConnectionState any_ice_state = 0;
924   GstWebRTCDTLSTransportState any_dtls_state = 0;
925   int i;
926 
927   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
928     GstWebRTCRTPTransceiver *rtp_trans =
929         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
930         i);
931     WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
932     TransportStream *stream = trans->stream;
933     GstWebRTCDTLSTransport *transport, *rtcp_transport;
934     GstWebRTCICEGatheringState ice_state;
935     GstWebRTCDTLSTransportState dtls_state;
936     gboolean rtcp_mux = FALSE;
937 
938     if (rtp_trans->stopped)
939       continue;
940     if (!rtp_trans->mid)
941       continue;
942 
943     g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
944     transport = webrtc_transceiver_get_dtls_transport (rtp_trans);
945 
946     /* get transport state */
947     g_object_get (transport, "state", &dtls_state, NULL);
948     any_dtls_state |= (1 << dtls_state);
949     g_object_get (transport->transport, "state", &ice_state, NULL);
950     any_ice_state |= (1 << ice_state);
951 
952     rtcp_transport = webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans);
953 
954     if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
955       g_object_get (rtcp_transport, "state", &dtls_state, NULL);
956       any_dtls_state |= (1 << dtls_state);
957       g_object_get (rtcp_transport->transport, "state", &ice_state, NULL);
958       any_ice_state |= (1 << ice_state);
959     }
960   }
961 
962   GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
963       "state: 0x%x", any_ice_state, any_dtls_state);
964 
965   /* The RTCPeerConnection object's [[ isClosed]] slot is true.  */
966   if (webrtc->priv->is_closed) {
967     GST_TRACE_OBJECT (webrtc, "returning closed");
968     return STATE (CLOSED);
969   }
970 
971   /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
972   if (any_ice_state & (1 << ICE_STATE (FAILED))) {
973     GST_TRACE_OBJECT (webrtc, "returning failed");
974     return STATE (FAILED);
975   }
976   if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
977     GST_TRACE_OBJECT (webrtc, "returning failed");
978     return STATE (FAILED);
979   }
980 
981   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting
982    * or checking state and none of them is in the failed state. */
983   if (any_ice_state & (1 << ICE_STATE (CHECKING))) {
984     GST_TRACE_OBJECT (webrtc, "returning connecting");
985     return STATE (CONNECTING);
986   }
987   if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) {
988     GST_TRACE_OBJECT (webrtc, "returning connecting");
989     return STATE (CONNECTING);
990   }
991 
992   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
993    * state and none of them are in the failed or connecting or checking state. */
994   if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
995     GST_TRACE_OBJECT (webrtc, "returning disconnected");
996     return STATE (DISCONNECTED);
997   }
998 
999   /* All RTCIceTransport's and RTCDtlsTransport's are in the connected,
1000    * completed or closed state and at least of them is in the connected or
1001    * completed state. */
1002   if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 <<
1003               ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED)))
1004       && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 <<
1005               DTLS_STATE (CLOSED)))
1006       && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 <<
1007               ICE_STATE (COMPLETED))
1008           || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) {
1009     GST_TRACE_OBJECT (webrtc, "returning connected");
1010     return STATE (CONNECTED);
1011   }
1012 
1013   /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state
1014    * and none of the transports are in the connecting, checking, failed or
1015    * disconnected state, or all transports are in the closed state. */
1016   if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) {
1017     GST_TRACE_OBJECT (webrtc, "returning new");
1018     return STATE (NEW);
1019   }
1020   if ((any_ice_state & (1 << ICE_STATE (NEW))
1021           || any_dtls_state & (1 << DTLS_STATE (NEW)))
1022       && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED)
1023               | (1 << ICE_STATE (DISCONNECTED))))
1024       && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 <<
1025               DTLS_STATE (FAILED)))) {
1026     GST_TRACE_OBJECT (webrtc, "returning new");
1027     return STATE (NEW);
1028   }
1029 
1030   GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new");
1031   return STATE (NEW);
1032 #undef DTLS_STATE
1033 #undef ICE_STATE
1034 #undef STATE
1035 }
1036 
1037 static void
_update_ice_gathering_state_task(GstWebRTCBin * webrtc,gpointer data)1038 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
1039 {
1040   GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
1041   GstWebRTCICEGatheringState new_state;
1042 
1043   new_state = _collate_ice_gathering_states (webrtc);
1044 
1045   if (new_state != webrtc->ice_gathering_state) {
1046     gchar *old_s, *new_s;
1047 
1048     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1049         old_state);
1050     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
1051         new_state);
1052     GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
1053         old_s, old_state, new_s, new_state);
1054     g_free (old_s);
1055     g_free (new_s);
1056 
1057     webrtc->ice_gathering_state = new_state;
1058     PC_UNLOCK (webrtc);
1059     g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
1060     PC_LOCK (webrtc);
1061   }
1062 }
1063 
1064 static void
_update_ice_gathering_state(GstWebRTCBin * webrtc)1065 _update_ice_gathering_state (GstWebRTCBin * webrtc)
1066 {
1067   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
1068       NULL);
1069 }
1070 
1071 static void
_update_ice_connection_state_task(GstWebRTCBin * webrtc,gpointer data)1072 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1073 {
1074   GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
1075   GstWebRTCICEConnectionState new_state;
1076 
1077   new_state = _collate_ice_connection_states (webrtc);
1078 
1079   if (new_state != old_state) {
1080     gchar *old_s, *new_s;
1081 
1082     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1083         old_state);
1084     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
1085         new_state);
1086     GST_INFO_OBJECT (webrtc,
1087         "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
1088         new_s, new_state);
1089     g_free (old_s);
1090     g_free (new_s);
1091 
1092     webrtc->ice_connection_state = new_state;
1093     PC_UNLOCK (webrtc);
1094     g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
1095     PC_LOCK (webrtc);
1096   }
1097 }
1098 
1099 static void
_update_ice_connection_state(GstWebRTCBin * webrtc)1100 _update_ice_connection_state (GstWebRTCBin * webrtc)
1101 {
1102   gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
1103       NULL);
1104 }
1105 
1106 static void
_update_peer_connection_state_task(GstWebRTCBin * webrtc,gpointer data)1107 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
1108 {
1109   GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
1110   GstWebRTCPeerConnectionState new_state;
1111 
1112   new_state = _collate_peer_connection_states (webrtc);
1113 
1114   if (new_state != old_state) {
1115     gchar *old_s, *new_s;
1116 
1117     old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1118         old_state);
1119     new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
1120         new_state);
1121     GST_INFO_OBJECT (webrtc,
1122         "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
1123         new_s, new_state);
1124     g_free (old_s);
1125     g_free (new_s);
1126 
1127     webrtc->peer_connection_state = new_state;
1128     PC_UNLOCK (webrtc);
1129     g_object_notify (G_OBJECT (webrtc), "connection-state");
1130     PC_LOCK (webrtc);
1131   }
1132 }
1133 
1134 static void
_update_peer_connection_state(GstWebRTCBin * webrtc)1135 _update_peer_connection_state (GstWebRTCBin * webrtc)
1136 {
1137   gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1138       NULL, NULL);
1139 }
1140 
1141 static gboolean
_all_sinks_have_caps(GstWebRTCBin * webrtc)1142 _all_sinks_have_caps (GstWebRTCBin * webrtc)
1143 {
1144   GList *l;
1145   gboolean res = FALSE;
1146 
1147   GST_OBJECT_LOCK (webrtc);
1148   l = GST_ELEMENT (webrtc)->pads;
1149   for (; l; l = g_list_next (l)) {
1150     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1151       continue;
1152     if (!GST_WEBRTC_BIN_PAD (l->data)->received_caps)
1153       goto done;
1154   }
1155 
1156   l = webrtc->priv->pending_pads;
1157   for (; l; l = g_list_next (l)) {
1158     if (!GST_IS_WEBRTC_BIN_PAD (l->data))
1159       goto done;
1160   }
1161 
1162   res = TRUE;
1163 
1164 done:
1165   GST_OBJECT_UNLOCK (webrtc);
1166   return res;
1167 }
1168 
1169 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1170 static gboolean
_check_if_negotiation_is_needed(GstWebRTCBin * webrtc)1171 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1172 {
1173   int i;
1174 
1175   GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1176 
1177   /* We can't negotiate until we have received caps on all our sink pads,
1178    * as we will need the ssrcs in our offer / answer */
1179   if (!_all_sinks_have_caps (webrtc)) {
1180     GST_LOG_OBJECT (webrtc,
1181         "no negotiation possible until caps have been received on all sink pads");
1182     return FALSE;
1183   }
1184 
1185   /* If any implementation-specific negotiation is required, as described at
1186    * the start of this section, return "true".
1187    * FIXME */
1188   /* FIXME: emit when input caps/format changes? */
1189 
1190   /* If connection has created any RTCDataChannel's, and no m= section has
1191    * been negotiated yet for data, return "true".
1192    * FIXME */
1193 
1194   if (!webrtc->current_local_description) {
1195     GST_LOG_OBJECT (webrtc, "no local description set");
1196     return TRUE;
1197   }
1198 
1199   if (!webrtc->current_remote_description) {
1200     GST_LOG_OBJECT (webrtc, "no remote description set");
1201     return TRUE;
1202   }
1203 
1204   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1205     GstWebRTCRTPTransceiver *trans;
1206 
1207     trans =
1208         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1209         i);
1210 
1211     if (trans->stopped) {
1212       /* FIXME: If t is stopped and is associated with an m= section according to
1213        * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1214        * rejected in connection's currentLocalDescription or
1215        * currentRemoteDescription , return "true". */
1216       GST_FIXME_OBJECT (webrtc,
1217           "check if the transceiver is rejected in descriptions");
1218     } else {
1219       const GstSDPMedia *media;
1220       GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1221 
1222       if (trans->mline == -1) {
1223         GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT,
1224             i, trans);
1225         return TRUE;
1226       }
1227       /* internal inconsistency */
1228       g_assert (trans->mline <
1229           gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1230       g_assert (trans->mline <
1231           gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1232 
1233       /* FIXME: msid handling
1234        * If t's direction is "sendrecv" or "sendonly", and the associated m=
1235        * section in connection's currentLocalDescription doesn't contain an
1236        * "a=msid" line, return "true". */
1237 
1238       media =
1239           gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1240           trans->mline);
1241       local_dir = _get_direction_from_media (media);
1242 
1243       media =
1244           gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1245           trans->mline);
1246       remote_dir = _get_direction_from_media (media);
1247 
1248       if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1249         /* If connection's currentLocalDescription if of type "offer", and
1250          * the direction of the associated m= section in neither the offer
1251          * nor answer matches t's direction, return "true". */
1252 
1253         if (local_dir != trans->direction && remote_dir != trans->direction) {
1254           GST_LOG_OBJECT (webrtc,
1255               "transceiver direction doesn't match description");
1256           return TRUE;
1257         }
1258       } else if (webrtc->current_local_description->type ==
1259           GST_WEBRTC_SDP_TYPE_ANSWER) {
1260         GstWebRTCRTPTransceiverDirection intersect_dir;
1261 
1262         /* If connection's currentLocalDescription if of type "answer", and
1263          * the direction of the associated m= section in the answer does not
1264          * match t's direction intersected with the offered direction (as
1265          * described in [JSEP] (section 5.3.1.)), return "true". */
1266 
1267         /* remote is the offer, local is the answer */
1268         intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1269 
1270         if (intersect_dir != trans->direction) {
1271           GST_LOG_OBJECT (webrtc,
1272               "transceiver direction doesn't match description");
1273           return TRUE;
1274         }
1275       }
1276     }
1277   }
1278 
1279   GST_LOG_OBJECT (webrtc, "no negotiation needed");
1280   return FALSE;
1281 }
1282 
1283 static void
_check_need_negotiation_task(GstWebRTCBin * webrtc,gpointer unused)1284 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1285 {
1286   if (webrtc->priv->need_negotiation) {
1287     GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1288     PC_UNLOCK (webrtc);
1289     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1290         0);
1291     PC_LOCK (webrtc);
1292   }
1293 }
1294 
1295 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1296 static void
_update_need_negotiation(GstWebRTCBin * webrtc)1297 _update_need_negotiation (GstWebRTCBin * webrtc)
1298 {
1299   /* If connection's [[isClosed]] slot is true, abort these steps. */
1300   if (webrtc->priv->is_closed)
1301     return;
1302   /* If connection's signaling state is not "stable", abort these steps. */
1303   if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1304     return;
1305 
1306   /* If the result of checking if negotiation is needed is "false", clear the
1307    * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1308    * to false, and abort these steps. */
1309   if (!_check_if_negotiation_is_needed (webrtc)) {
1310     webrtc->priv->need_negotiation = FALSE;
1311     return;
1312   }
1313   /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1314   if (webrtc->priv->need_negotiation)
1315     return;
1316   /* Set connection's [[needNegotiation]] slot to true. */
1317   webrtc->priv->need_negotiation = TRUE;
1318   /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1319    * true, fire a simple event named negotiationneeded at connection. */
1320   gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1321       NULL);
1322 }
1323 
1324 static GstCaps *
_find_codec_preferences(GstWebRTCBin * webrtc,GstWebRTCRTPTransceiver * trans,GstPadDirection direction,guint media_idx)1325 _find_codec_preferences (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * trans,
1326     GstPadDirection direction, guint media_idx)
1327 {
1328   GstCaps *ret = NULL;
1329 
1330   GST_LOG_OBJECT (webrtc, "retreiving codec preferences from %" GST_PTR_FORMAT,
1331       trans);
1332 
1333   if (trans && trans->codec_preferences) {
1334     GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1335         trans->codec_preferences);
1336     ret = gst_caps_ref (trans->codec_preferences);
1337   } else {
1338     GstWebRTCBinPad *pad = _find_pad_for_mline (webrtc, direction, media_idx);
1339     if (pad) {
1340       GstCaps *caps = NULL;
1341 
1342       if (pad->received_caps) {
1343         caps = gst_caps_ref (pad->received_caps);
1344       } else if ((caps = gst_pad_get_current_caps (GST_PAD (pad)))) {
1345         GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1346             caps);
1347       } else {
1348         if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
1349           GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1350               caps);
1351       }
1352       if (caps)
1353         ret = caps;
1354       gst_object_unref (pad);
1355     }
1356   }
1357 
1358   return ret;
1359 }
1360 
1361 static GstCaps *
_add_supported_attributes_to_caps(GstWebRTCBin * webrtc,WebRTCTransceiver * trans,const GstCaps * caps)1362 _add_supported_attributes_to_caps (GstWebRTCBin * webrtc,
1363     WebRTCTransceiver * trans, const GstCaps * caps)
1364 {
1365   GstCaps *ret;
1366   guint i;
1367 
1368   ret = gst_caps_make_writable (caps);
1369 
1370   for (i = 0; i < gst_caps_get_size (ret); i++) {
1371     GstStructure *s = gst_caps_get_structure (ret, i);
1372 
1373     if (trans->do_nack)
1374       if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1375         gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1376 
1377     if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1378       gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1379     /* FIXME: is this needed? */
1380     /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1381        gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1382 
1383     /* FIXME: codec-specific paramters? */
1384   }
1385 
1386   return ret;
1387 }
1388 
1389 static void
_on_ice_transport_notify_state(GstWebRTCICETransport * transport,GParamSpec * pspec,GstWebRTCBin * webrtc)1390 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1391     GParamSpec * pspec, GstWebRTCBin * webrtc)
1392 {
1393   _update_ice_connection_state (webrtc);
1394   _update_peer_connection_state (webrtc);
1395 }
1396 
1397 static void
_on_ice_transport_notify_gathering_state(GstWebRTCICETransport * transport,GParamSpec * pspec,GstWebRTCBin * webrtc)1398 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1399     GParamSpec * pspec, GstWebRTCBin * webrtc)
1400 {
1401   _update_ice_gathering_state (webrtc);
1402 }
1403 
1404 static void
_on_dtls_transport_notify_state(GstWebRTCDTLSTransport * transport,GParamSpec * pspec,GstWebRTCBin * webrtc)1405 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1406     GParamSpec * pspec, GstWebRTCBin * webrtc)
1407 {
1408   _update_peer_connection_state (webrtc);
1409 }
1410 
1411 static WebRTCTransceiver *
_create_webrtc_transceiver(GstWebRTCBin * webrtc,GstWebRTCRTPTransceiverDirection direction,guint mline)1412 _create_webrtc_transceiver (GstWebRTCBin * webrtc,
1413     GstWebRTCRTPTransceiverDirection direction, guint mline)
1414 {
1415   WebRTCTransceiver *trans;
1416   GstWebRTCRTPTransceiver *rtp_trans;
1417   GstWebRTCRTPSender *sender;
1418   GstWebRTCRTPReceiver *receiver;
1419 
1420   sender = gst_webrtc_rtp_sender_new ();
1421   receiver = gst_webrtc_rtp_receiver_new ();
1422   trans = webrtc_transceiver_new (webrtc, sender, receiver);
1423   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1424   rtp_trans->direction = direction;
1425   rtp_trans->mline = mline;
1426 
1427   g_array_append_val (webrtc->priv->transceivers, trans);
1428 
1429   gst_object_unref (sender);
1430   gst_object_unref (receiver);
1431 
1432   g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL],
1433       0, trans);
1434 
1435   return trans;
1436 }
1437 
1438 static TransportStream *
_create_transport_channel(GstWebRTCBin * webrtc,guint session_id)1439 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1440 {
1441   GstWebRTCDTLSTransport *transport;
1442   TransportStream *ret;
1443 
1444   /* FIXME: how to parametrize the sender and the receiver */
1445   ret = transport_stream_new (webrtc, session_id);
1446   transport = ret->transport;
1447 
1448   g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1449       G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1450   g_signal_connect (G_OBJECT (transport->transport),
1451       "notify::gathering-state",
1452       G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1453   g_signal_connect (G_OBJECT (transport), "notify::state",
1454       G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1455 
1456   if ((transport = ret->rtcp_transport)) {
1457     g_signal_connect (G_OBJECT (transport->transport),
1458         "notify::state", G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1459     g_signal_connect (G_OBJECT (transport->transport),
1460         "notify::gathering-state",
1461         G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1462     g_signal_connect (G_OBJECT (transport), "notify::state",
1463         G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1464   }
1465 
1466   GST_TRACE_OBJECT (webrtc,
1467       "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1468 
1469   return ret;
1470 }
1471 
1472 static TransportStream *
_get_or_create_rtp_transport_channel(GstWebRTCBin * webrtc,guint session_id)1473 _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1474 {
1475   TransportStream *ret;
1476   gchar *pad_name;
1477 
1478   ret = _find_transport_for_session (webrtc, session_id);
1479 
1480   if (!ret) {
1481     ret = _create_transport_channel (webrtc, session_id);
1482     gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1483     gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1484     g_array_append_val (webrtc->priv->transports, ret);
1485 
1486     pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1487     if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1488             GST_ELEMENT (webrtc->rtpbin), pad_name))
1489       g_warn_if_reached ();
1490     g_free (pad_name);
1491 
1492     pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1493     if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1494             GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1495       g_warn_if_reached ();
1496     g_free (pad_name);
1497   }
1498 
1499   gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1500   gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1501 
1502   return ret;
1503 }
1504 
1505 /* this is called from the webrtc thread with the pc lock held */
1506 static void
_on_data_channel_ready_state(GstWebRTCDataChannel * channel,GParamSpec * pspec,GstWebRTCBin * webrtc)1507 _on_data_channel_ready_state (GstWebRTCDataChannel * channel,
1508     GParamSpec * pspec, GstWebRTCBin * webrtc)
1509 {
1510   GstWebRTCDataChannelState ready_state;
1511   guint i;
1512 
1513   g_object_get (channel, "ready-state", &ready_state, NULL);
1514 
1515   if (ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) {
1516     gboolean found = FALSE;
1517 
1518     for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) {
1519       GstWebRTCDataChannel *c;
1520 
1521       c = g_array_index (webrtc->priv->pending_data_channels,
1522           GstWebRTCDataChannel *, i);
1523       if (c == channel) {
1524         found = TRUE;
1525         g_array_remove_index (webrtc->priv->pending_data_channels, i);
1526         break;
1527       }
1528     }
1529     if (found == FALSE) {
1530       GST_FIXME_OBJECT (webrtc, "Received open for unknown data channel");
1531       return;
1532     }
1533 
1534     g_array_append_val (webrtc->priv->data_channels, channel);
1535 
1536     g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
1537         gst_object_ref (channel));
1538   }
1539 }
1540 
1541 static void
_link_data_channel_to_sctp(GstWebRTCBin * webrtc,GstWebRTCDataChannel * channel)1542 _link_data_channel_to_sctp (GstWebRTCBin * webrtc,
1543     GstWebRTCDataChannel * channel)
1544 {
1545   if (webrtc->priv->sctp_transport && !channel->sctp_transport) {
1546     gint id;
1547 
1548     g_object_get (channel, "id", &id, NULL);
1549 
1550     if (webrtc->priv->sctp_transport->association_established && id != -1) {
1551       gchar *pad_name;
1552 
1553       gst_webrtc_data_channel_set_sctp_transport (channel,
1554           webrtc->priv->sctp_transport);
1555       pad_name = g_strdup_printf ("sink_%u", id);
1556       if (!gst_element_link_pads (channel->appsrc, "src",
1557               channel->sctp_transport->sctpenc, pad_name))
1558         g_warn_if_reached ();
1559       g_free (pad_name);
1560     }
1561   }
1562 }
1563 
1564 static void
_on_sctpdec_pad_added(GstElement * sctpdec,GstPad * pad,GstWebRTCBin * webrtc)1565 _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad,
1566     GstWebRTCBin * webrtc)
1567 {
1568   GstWebRTCDataChannel *channel;
1569   guint stream_id;
1570   GstPad *sink_pad;
1571 
1572   if (sscanf (GST_PAD_NAME (pad), "src_%u", &stream_id) != 1)
1573     return;
1574 
1575   PC_LOCK (webrtc);
1576   channel = _find_data_channel_for_id (webrtc, stream_id);
1577   if (!channel) {
1578     channel = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, NULL);
1579     channel->id = stream_id;
1580     channel->webrtcbin = webrtc;
1581 
1582     gst_bin_add (GST_BIN (webrtc), channel->appsrc);
1583     gst_bin_add (GST_BIN (webrtc), channel->appsink);
1584 
1585     gst_element_sync_state_with_parent (channel->appsrc);
1586     gst_element_sync_state_with_parent (channel->appsink);
1587 
1588     _link_data_channel_to_sctp (webrtc, channel);
1589 
1590     g_array_append_val (webrtc->priv->pending_data_channels, channel);
1591   }
1592 
1593   g_signal_connect (channel, "notify::ready-state",
1594       G_CALLBACK (_on_data_channel_ready_state), webrtc);
1595 
1596   sink_pad = gst_element_get_static_pad (channel->appsink, "sink");
1597   if (gst_pad_link (pad, sink_pad) != GST_PAD_LINK_OK)
1598     GST_WARNING_OBJECT (channel, "Failed to link sctp pad %s with channel %"
1599         GST_PTR_FORMAT, GST_PAD_NAME (pad), channel);
1600   gst_object_unref (sink_pad);
1601   PC_UNLOCK (webrtc);
1602 }
1603 
1604 static void
_on_sctp_state_notify(GstWebRTCSCTPTransport * sctp,GParamSpec * pspec,GstWebRTCBin * webrtc)1605 _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec,
1606     GstWebRTCBin * webrtc)
1607 {
1608   GstWebRTCSCTPTransportState state;
1609 
1610   g_object_get (sctp, "state", &state, NULL);
1611 
1612   if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) {
1613     int i;
1614 
1615     PC_LOCK (webrtc);
1616     GST_DEBUG_OBJECT (webrtc, "SCTP association established");
1617 
1618     for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1619       GstWebRTCDataChannel *channel;
1620 
1621       channel =
1622           g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
1623           i);
1624 
1625       _link_data_channel_to_sctp (webrtc, channel);
1626 
1627       if (!channel->negotiated && !channel->opened)
1628         gst_webrtc_data_channel_start_negotiation (channel);
1629     }
1630     PC_UNLOCK (webrtc);
1631   }
1632 }
1633 
1634 static TransportStream *
_get_or_create_data_channel_transports(GstWebRTCBin * webrtc,guint session_id)1635 _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
1636 {
1637   if (!webrtc->priv->data_channel_transport) {
1638     TransportStream *stream;
1639     GstWebRTCSCTPTransport *sctp_transport;
1640     int i;
1641 
1642     stream = _find_transport_for_session (webrtc, session_id);
1643 
1644     if (!stream) {
1645       stream = _create_transport_channel (webrtc, session_id);
1646       gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin));
1647       gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin));
1648       g_array_append_val (webrtc->priv->transports, stream);
1649     }
1650 
1651     webrtc->priv->data_channel_transport = stream;
1652 
1653     g_object_set (stream, "rtcp-mux", TRUE, NULL);
1654 
1655     if (!(sctp_transport = webrtc->priv->sctp_transport)) {
1656       sctp_transport = gst_webrtc_sctp_transport_new ();
1657       sctp_transport->transport =
1658           g_object_ref (webrtc->priv->data_channel_transport->transport);
1659       sctp_transport->webrtcbin = webrtc;
1660 
1661       gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec);
1662       gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc);
1663     }
1664 
1665     g_signal_connect (sctp_transport->sctpdec, "pad-added",
1666         G_CALLBACK (_on_sctpdec_pad_added), webrtc);
1667     g_signal_connect (sctp_transport, "notify::state",
1668         G_CALLBACK (_on_sctp_state_notify), webrtc);
1669 
1670     if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src",
1671             GST_ELEMENT (sctp_transport->sctpdec), "sink"))
1672       g_warn_if_reached ();
1673 
1674     if (!gst_element_link_pads (GST_ELEMENT (sctp_transport->sctpenc), "src",
1675             GST_ELEMENT (stream->send_bin), "data_sink"))
1676       g_warn_if_reached ();
1677 
1678     for (i = 0; i < webrtc->priv->data_channels->len; i++) {
1679       GstWebRTCDataChannel *channel;
1680 
1681       channel =
1682           g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *,
1683           i);
1684 
1685       _link_data_channel_to_sctp (webrtc, channel);
1686     }
1687 
1688     gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin));
1689     gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
1690 
1691     if (!webrtc->priv->sctp_transport) {
1692       gst_element_sync_state_with_parent (GST_ELEMENT
1693           (sctp_transport->sctpdec));
1694       gst_element_sync_state_with_parent (GST_ELEMENT
1695           (sctp_transport->sctpenc));
1696     }
1697 
1698     webrtc->priv->sctp_transport = sctp_transport;
1699   }
1700 
1701   return webrtc->priv->data_channel_transport;
1702 }
1703 
1704 static TransportStream *
_get_or_create_transport_stream(GstWebRTCBin * webrtc,guint session_id,gboolean is_datachannel)1705 _get_or_create_transport_stream (GstWebRTCBin * webrtc, guint session_id,
1706     gboolean is_datachannel)
1707 {
1708   if (is_datachannel)
1709     return _get_or_create_data_channel_transports (webrtc, session_id);
1710   else
1711     return _get_or_create_rtp_transport_channel (webrtc, session_id);
1712 }
1713 
1714 static guint
g_array_find_uint(GArray * array,guint val)1715 g_array_find_uint (GArray * array, guint val)
1716 {
1717   guint i;
1718 
1719   for (i = 0; i < array->len; i++) {
1720     if (g_array_index (array, guint, i) == val)
1721       return i;
1722   }
1723 
1724   return G_MAXUINT;
1725 }
1726 
1727 static gboolean
_pick_available_pt(GArray * reserved_pts,guint * i)1728 _pick_available_pt (GArray * reserved_pts, guint * i)
1729 {
1730   gboolean ret = FALSE;
1731 
1732   for (*i = 96; *i <= 127; (*i)++) {
1733     if (g_array_find_uint (reserved_pts, *i) == G_MAXUINT) {
1734       g_array_append_val (reserved_pts, *i);
1735       ret = TRUE;
1736       break;
1737     }
1738   }
1739 
1740   return ret;
1741 }
1742 
1743 static gboolean
_pick_fec_payload_types(GstWebRTCBin * webrtc,WebRTCTransceiver * trans,GArray * reserved_pts,gint clockrate,gint * rtx_target_pt,GstSDPMedia * media)1744 _pick_fec_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
1745     GArray * reserved_pts, gint clockrate, gint * rtx_target_pt,
1746     GstSDPMedia * media)
1747 {
1748   gboolean ret = TRUE;
1749 
1750   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
1751     goto done;
1752 
1753   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_ULP_RED && clockrate != -1) {
1754     guint pt;
1755     gchar *str;
1756 
1757     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1758       goto done;
1759 
1760     /* https://tools.ietf.org/html/rfc5109#section-14.1 */
1761 
1762     str = g_strdup_printf ("%u", pt);
1763     gst_sdp_media_add_format (media, str);
1764     g_free (str);
1765     str = g_strdup_printf ("%u red/%d", pt, clockrate);
1766     gst_sdp_media_add_attribute (media, "rtpmap", str);
1767     g_free (str);
1768 
1769     *rtx_target_pt = pt;
1770 
1771     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1772       goto done;
1773 
1774     str = g_strdup_printf ("%u", pt);
1775     gst_sdp_media_add_format (media, str);
1776     g_free (str);
1777     str = g_strdup_printf ("%u ulpfec/%d", pt, clockrate);
1778     gst_sdp_media_add_attribute (media, "rtpmap", str);
1779     g_free (str);
1780   }
1781 
1782 done:
1783   return ret;
1784 }
1785 
1786 static gboolean
_pick_rtx_payload_types(GstWebRTCBin * webrtc,WebRTCTransceiver * trans,GArray * reserved_pts,gint clockrate,gint target_pt,guint target_ssrc,GstSDPMedia * media)1787 _pick_rtx_payload_types (GstWebRTCBin * webrtc, WebRTCTransceiver * trans,
1788     GArray * reserved_pts, gint clockrate, gint target_pt, guint target_ssrc,
1789     GstSDPMedia * media)
1790 {
1791   gboolean ret = TRUE;
1792 
1793   if (trans->local_rtx_ssrc_map)
1794     gst_structure_free (trans->local_rtx_ssrc_map);
1795 
1796   trans->local_rtx_ssrc_map =
1797       gst_structure_new_empty ("application/x-rtp-ssrc-map");
1798 
1799   if (trans->do_nack) {
1800     guint pt;
1801     gchar *str;
1802 
1803     if (!(ret = _pick_available_pt (reserved_pts, &pt)))
1804       goto done;
1805 
1806     /* https://tools.ietf.org/html/rfc4588#section-8.6 */
1807 
1808     str = g_strdup_printf ("%u", target_ssrc);
1809     gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
1810         g_random_int (), NULL);
1811     g_free (str);
1812 
1813     str = g_strdup_printf ("%u", pt);
1814     gst_sdp_media_add_format (media, str);
1815     g_free (str);
1816 
1817     str = g_strdup_printf ("%u rtx/%d", pt, clockrate);
1818     gst_sdp_media_add_attribute (media, "rtpmap", str);
1819     g_free (str);
1820 
1821     str = g_strdup_printf ("%u apt=%d", pt, target_pt);
1822     gst_sdp_media_add_attribute (media, "fmtp", str);
1823     g_free (str);
1824   }
1825 
1826 done:
1827   return ret;
1828 }
1829 
1830 /* https://tools.ietf.org/html/rfc5576#section-4.2 */
1831 static gboolean
_media_add_rtx_ssrc_group(GQuark field_id,const GValue * value,GstSDPMedia * media)1832 _media_add_rtx_ssrc_group (GQuark field_id, const GValue * value,
1833     GstSDPMedia * media)
1834 {
1835   gchar *str;
1836 
1837   str =
1838       g_strdup_printf ("FID %s %u", g_quark_to_string (field_id),
1839       g_value_get_uint (value));
1840   gst_sdp_media_add_attribute (media, "ssrc-group", str);
1841 
1842   g_free (str);
1843 
1844   return TRUE;
1845 }
1846 
1847 typedef struct
1848 {
1849   GstSDPMedia *media;
1850   GstWebRTCBin *webrtc;
1851   WebRTCTransceiver *trans;
1852 } RtxSsrcData;
1853 
1854 static gboolean
_media_add_rtx_ssrc(GQuark field_id,const GValue * value,RtxSsrcData * data)1855 _media_add_rtx_ssrc (GQuark field_id, const GValue * value, RtxSsrcData * data)
1856 {
1857   gchar *str;
1858   GstStructure *sdes;
1859   const gchar *cname;
1860 
1861   g_object_get (data->webrtc->rtpbin, "sdes", &sdes, NULL);
1862   /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
1863   cname = gst_structure_get_string (sdes, "cname");
1864 
1865   /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
1866   str =
1867       g_strdup_printf ("%u msid:%s %s", g_value_get_uint (value),
1868       cname, GST_OBJECT_NAME (data->trans));
1869   gst_sdp_media_add_attribute (data->media, "ssrc", str);
1870   g_free (str);
1871 
1872   str = g_strdup_printf ("%u cname:%s", g_value_get_uint (value), cname);
1873   gst_sdp_media_add_attribute (data->media, "ssrc", str);
1874   g_free (str);
1875 
1876   gst_structure_free (sdes);
1877 
1878   return TRUE;
1879 }
1880 
1881 static void
_media_add_ssrcs(GstSDPMedia * media,GstCaps * caps,GstWebRTCBin * webrtc,WebRTCTransceiver * trans)1882 _media_add_ssrcs (GstSDPMedia * media, GstCaps * caps, GstWebRTCBin * webrtc,
1883     WebRTCTransceiver * trans)
1884 {
1885   guint i;
1886   RtxSsrcData data = { media, webrtc, trans };
1887   const gchar *cname;
1888   GstStructure *sdes;
1889 
1890   g_object_get (webrtc->rtpbin, "sdes", &sdes, NULL);
1891   /* http://www.freesoft.org/CIE/RFC/1889/24.htm */
1892   cname = gst_structure_get_string (sdes, "cname");
1893 
1894   if (trans->local_rtx_ssrc_map)
1895     gst_structure_foreach (trans->local_rtx_ssrc_map,
1896         (GstStructureForeachFunc) _media_add_rtx_ssrc_group, media);
1897 
1898   for (i = 0; i < gst_caps_get_size (caps); i++) {
1899     const GstStructure *s = gst_caps_get_structure (caps, i);
1900     guint ssrc;
1901 
1902     if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
1903       gchar *str;
1904 
1905       /* https://tools.ietf.org/html/draft-ietf-mmusic-msid-16 */
1906       str =
1907           g_strdup_printf ("%u msid:%s %s", ssrc, cname,
1908           GST_OBJECT_NAME (trans));
1909       gst_sdp_media_add_attribute (media, "ssrc", str);
1910       g_free (str);
1911 
1912       str = g_strdup_printf ("%u cname:%s", ssrc, cname);
1913       gst_sdp_media_add_attribute (media, "ssrc", str);
1914       g_free (str);
1915     }
1916   }
1917 
1918   gst_structure_free (sdes);
1919 
1920   if (trans->local_rtx_ssrc_map)
1921     gst_structure_foreach (trans->local_rtx_ssrc_map,
1922         (GstStructureForeachFunc) _media_add_rtx_ssrc, &data);
1923 }
1924 
1925 static void
_add_fingerprint_to_media(GstWebRTCDTLSTransport * transport,GstSDPMedia * media)1926 _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport,
1927     GstSDPMedia * media)
1928 {
1929   gchar *cert, *fingerprint, *val;
1930 
1931   g_object_get (transport, "certificate", &cert, NULL);
1932 
1933   fingerprint =
1934       _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
1935   g_free (cert);
1936   val =
1937       g_strdup_printf ("%s %s",
1938       _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
1939   g_free (fingerprint);
1940 
1941   gst_sdp_media_add_attribute (media, "fingerprint", val);
1942   g_free (val);
1943 }
1944 
1945 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
1946 static gboolean
sdp_media_from_transceiver(GstWebRTCBin * webrtc,GstSDPMedia * media,GstWebRTCRTPTransceiver * trans,GstWebRTCSDPType type,guint media_idx,GString * bundled_mids,guint bundle_idx,gboolean bundle_only,GArray * reserved_pts)1947 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
1948     GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx,
1949     GString * bundled_mids, guint bundle_idx, gboolean bundle_only,
1950     GArray * reserved_pts)
1951 {
1952   /* TODO:
1953    * rtp header extensions
1954    * ice attributes
1955    * rtx
1956    * fec
1957    * msid-semantics
1958    * msid
1959    * dtls fingerprints
1960    * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
1961    */
1962   gchar *direction, *sdp_mid;
1963   GstCaps *caps;
1964   int i;
1965 
1966   /* "An m= section is generated for each RtpTransceiver that has been added
1967    * to the Bin, excluding any stopped RtpTransceivers." */
1968   if (trans->stopped)
1969     return FALSE;
1970   if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
1971       || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
1972     return FALSE;
1973 
1974   gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0);
1975   gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
1976   gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
1977 
1978   if (bundle_only) {
1979     gst_sdp_media_add_attribute (media, "bundle-only", NULL);
1980   }
1981 
1982   /* FIXME: negotiate this */
1983   /* FIXME: when bundle_only, these should not be added:
1984    * https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-52#section-7.1.3
1985    * However, this causes incompatibilities with current versions
1986    * of the major browsers */
1987   gst_sdp_media_add_attribute (media, "rtcp-mux", "");
1988   gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
1989 
1990   direction =
1991       _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1992       trans->direction);
1993   gst_sdp_media_add_attribute (media, direction, "");
1994   g_free (direction);
1995 
1996   if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
1997     caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
1998     caps =
1999         _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans),
2000         caps);
2001   } else if (type == GST_WEBRTC_SDP_TYPE_ANSWER) {
2002     caps = _find_codec_preferences (webrtc, trans, GST_PAD_SRC, media_idx);
2003     /* FIXME: add rtcp-fb paramaters */
2004   } else {
2005     g_assert_not_reached ();
2006   }
2007 
2008   if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
2009     GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
2010     if (caps)
2011       gst_caps_unref (caps);
2012     return FALSE;
2013   }
2014 
2015   for (i = 0; i < gst_caps_get_size (caps); i++) {
2016     GstCaps *format = gst_caps_new_empty ();
2017     const GstStructure *s = gst_caps_get_structure (caps, i);
2018 
2019     gst_caps_append_structure (format, gst_structure_copy (s));
2020 
2021     GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
2022         " to %u-th media", i, format, media_idx);
2023 
2024     /* this only looks at the first structure so we loop over the given caps
2025      * and add each structure inside it piecemeal */
2026     gst_sdp_media_set_media_from_caps (format, media);
2027 
2028     gst_caps_unref (format);
2029   }
2030 
2031   if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
2032     const GstStructure *s = gst_caps_get_structure (caps, 0);
2033     gint clockrate = -1;
2034     gint rtx_target_pt;
2035     gint original_rtx_target_pt;        /* Workaround chrome bug: https://bugs.chromium.org/p/webrtc/issues/detail?id=6196 */
2036     guint rtx_target_ssrc = -1;
2037 
2038     if (gst_structure_get_int (s, "payload", &rtx_target_pt) &&
2039         webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2040       g_array_append_val (reserved_pts, rtx_target_pt);
2041 
2042     original_rtx_target_pt = rtx_target_pt;
2043 
2044     if (!gst_structure_get_int (s, "clock-rate", &clockrate))
2045       GST_WARNING_OBJECT (webrtc,
2046           "Caps %" GST_PTR_FORMAT " are missing clock-rate", caps);
2047     if (!gst_structure_get_uint (s, "ssrc", &rtx_target_ssrc))
2048       GST_WARNING_OBJECT (webrtc, "Caps %" GST_PTR_FORMAT " are missing ssrc",
2049           caps);
2050 
2051     _pick_fec_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2052         clockrate, &rtx_target_pt, media);
2053     _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2054         clockrate, rtx_target_pt, rtx_target_ssrc, media);
2055     if (original_rtx_target_pt != rtx_target_pt)
2056       _pick_rtx_payload_types (webrtc, WEBRTC_TRANSCEIVER (trans), reserved_pts,
2057           clockrate, original_rtx_target_pt, rtx_target_ssrc, media);
2058   }
2059 
2060   _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans));
2061 
2062   /* Some identifier; we also add the media name to it so it's identifiable */
2063   sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
2064       webrtc->priv->media_counter++);
2065   gst_sdp_media_add_attribute (media, "mid", sdp_mid);
2066   g_free (sdp_mid);
2067 
2068   if (trans->sender) {
2069     if (!trans->sender->transport) {
2070       TransportStream *item;
2071 
2072       item =
2073           _get_or_create_transport_stream (webrtc,
2074           bundled_mids ? bundle_idx : media_idx, FALSE);
2075 
2076       webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
2077     }
2078 
2079     _add_fingerprint_to_media (trans->sender->transport, media);
2080   }
2081 
2082   gst_caps_unref (caps);
2083 
2084   return TRUE;
2085 }
2086 
2087 static void
gather_pad_pt(GstWebRTCBinPad * pad,GArray * reserved_pts)2088 gather_pad_pt (GstWebRTCBinPad * pad, GArray * reserved_pts)
2089 {
2090   if (pad->received_caps) {
2091     GstStructure *s = gst_caps_get_structure (pad->received_caps, 0);
2092     gint pt;
2093 
2094     if (gst_structure_get_int (s, "payload", &pt)) {
2095       g_array_append_val (reserved_pts, pt);
2096     }
2097   }
2098 }
2099 
2100 static GArray *
gather_reserved_pts(GstWebRTCBin * webrtc)2101 gather_reserved_pts (GstWebRTCBin * webrtc)
2102 {
2103   GstElement *element = GST_ELEMENT (webrtc);
2104   GArray *reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2105 
2106   GST_OBJECT_LOCK (webrtc);
2107   g_list_foreach (element->sinkpads, (GFunc) gather_pad_pt, reserved_pts);
2108   g_list_foreach (webrtc->priv->pending_pads, (GFunc) gather_pad_pt,
2109       reserved_pts);
2110   GST_OBJECT_UNLOCK (webrtc);
2111 
2112   return reserved_pts;
2113 }
2114 
2115 /* TODO: use the options argument */
2116 static GstSDPMessage *
_create_offer_task(GstWebRTCBin * webrtc,const GstStructure * options)2117 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
2118 {
2119   GstSDPMessage *ret;
2120   int i;
2121   GString *bundled_mids = NULL;
2122   gchar *bundle_ufrag = NULL;
2123   gchar *bundle_pwd = NULL;
2124   GArray *reserved_pts = NULL;
2125 
2126   gst_sdp_message_new (&ret);
2127 
2128   gst_sdp_message_set_version (ret, "0");
2129   {
2130     /* FIXME: session id and version need special handling depending on the state we're in */
2131     gchar *sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
2132     gst_sdp_message_set_origin (ret, "-", sess_id, "0", "IN", "IP4", "0.0.0.0");
2133     g_free (sess_id);
2134   }
2135   gst_sdp_message_set_session_name (ret, "-");
2136   gst_sdp_message_add_time (ret, "0", "0", NULL);
2137   gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
2138 
2139   if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE) {
2140     bundled_mids = g_string_new ("BUNDLE");
2141   } else if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_COMPAT) {
2142     bundled_mids = g_string_new ("BUNDLE");
2143   }
2144 
2145   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2146     _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2147     reserved_pts = gather_reserved_pts (webrtc);
2148   }
2149 
2150   /* for each rtp transceiver */
2151   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2152     GstWebRTCRTPTransceiver *trans;
2153     GstSDPMedia media = { 0, };
2154     gchar *ufrag, *pwd;
2155     gboolean bundle_only = bundled_mids
2156         && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2157         && i != 0;
2158 
2159     trans =
2160         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
2161         i);
2162 
2163     gst_sdp_media_init (&media);
2164     /* mandated by JSEP */
2165     gst_sdp_media_add_attribute (&media, "setup", "actpass");
2166 
2167     /* FIXME: only needed when restarting ICE */
2168     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2169       reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint));
2170       _generate_ice_credentials (&ufrag, &pwd);
2171     } else {
2172       ufrag = g_strdup (bundle_ufrag);
2173       pwd = g_strdup (bundle_pwd);
2174     }
2175 
2176     gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag);
2177     gst_sdp_media_add_attribute (&media, "ice-pwd", pwd);
2178     g_free (ufrag);
2179     g_free (pwd);
2180 
2181     g_assert (reserved_pts != NULL);
2182 
2183     if (sdp_media_from_transceiver (webrtc, &media, trans,
2184             GST_WEBRTC_SDP_TYPE_OFFER, i, bundled_mids, 0, bundle_only,
2185             reserved_pts)) {
2186       if (bundled_mids) {
2187         const gchar *mid = gst_sdp_media_get_attribute_val (&media, "mid");
2188 
2189         g_assert (mid);
2190         g_string_append_printf (bundled_mids, " %s", mid);
2191       }
2192       gst_sdp_message_add_media (ret, &media);
2193     } else {
2194       gst_sdp_media_uninit (&media);
2195     }
2196 
2197     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2198       g_array_free (reserved_pts, TRUE);
2199     }
2200   }
2201 
2202   if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2203     g_array_free (reserved_pts, TRUE);
2204   }
2205 
2206   /* add data channel support */
2207   if (webrtc->priv->data_channels->len > 0) {
2208     GstSDPMedia media = { 0, };
2209     gchar *ufrag, *pwd, *sdp_mid;
2210     gboolean bundle_only = bundled_mids
2211         && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE
2212         && webrtc->priv->transceivers->len != 0;
2213 
2214     gst_sdp_media_init (&media);
2215     /* mandated by JSEP */
2216     gst_sdp_media_add_attribute (&media, "setup", "actpass");
2217 
2218     /* FIXME: only needed when restarting ICE */
2219     if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) {
2220       _generate_ice_credentials (&ufrag, &pwd);
2221     } else {
2222       ufrag = g_strdup (bundle_ufrag);
2223       pwd = g_strdup (bundle_pwd);
2224     }
2225     gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag);
2226     gst_sdp_media_add_attribute (&media, "ice-pwd", pwd);
2227     g_free (ufrag);
2228     g_free (pwd);
2229 
2230     gst_sdp_media_set_media (&media, "application");
2231     gst_sdp_media_set_port_info (&media, bundle_only ? 0 : 9, 0);
2232     gst_sdp_media_set_proto (&media, "UDP/DTLS/SCTP");
2233     gst_sdp_media_add_connection (&media, "IN", "IP4", "0.0.0.0", 0, 0);
2234     gst_sdp_media_add_format (&media, "webrtc-datachannel");
2235 
2236     if (bundle_only)
2237       gst_sdp_media_add_attribute (&media, "bundle-only", NULL);
2238 
2239     sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (&media),
2240         webrtc->priv->media_counter++);
2241     gst_sdp_media_add_attribute (&media, "mid", sdp_mid);
2242     if (bundled_mids)
2243       g_string_append_printf (bundled_mids, " %s", sdp_mid);
2244     g_free (sdp_mid);
2245 
2246     /* FIXME: negotiate this properly */
2247     gst_sdp_media_add_attribute (&media, "sctp-port", "5000");
2248 
2249     _get_or_create_data_channel_transports (webrtc,
2250         bundled_mids ? 0 : webrtc->priv->transceivers->len);
2251     _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, &media);
2252 
2253     gst_sdp_message_add_media (ret, &media);
2254   }
2255 
2256   if (bundled_mids) {
2257     gchar *mids = g_string_free (bundled_mids, FALSE);
2258 
2259     gst_sdp_message_add_attribute (ret, "group", mids);
2260     g_free (mids);
2261   }
2262 
2263   if (bundle_ufrag)
2264     g_free (bundle_ufrag);
2265 
2266   if (bundle_pwd)
2267     g_free (bundle_pwd);
2268 
2269   /* FIXME: pre-emptively setup receiving elements when needed */
2270 
2271   /* XXX: only true for the initial offerer */
2272   g_object_set (webrtc->priv->ice, "controller", TRUE, NULL);
2273 
2274   return ret;
2275 }
2276 
2277 static void
_media_add_fec(GstSDPMedia * media,WebRTCTransceiver * trans,GstCaps * caps,gint * rtx_target_pt)2278 _media_add_fec (GstSDPMedia * media, WebRTCTransceiver * trans, GstCaps * caps,
2279     gint * rtx_target_pt)
2280 {
2281   guint i;
2282 
2283   if (trans->fec_type == GST_WEBRTC_FEC_TYPE_NONE)
2284     return;
2285 
2286   for (i = 0; i < gst_caps_get_size (caps); i++) {
2287     const GstStructure *s = gst_caps_get_structure (caps, i);
2288 
2289     if (gst_structure_has_name (s, "application/x-rtp")) {
2290       const gchar *encoding_name =
2291           gst_structure_get_string (s, "encoding-name");
2292       gint clock_rate;
2293       gint pt;
2294 
2295       if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
2296           gst_structure_get_int (s, "payload", &pt)) {
2297         if (!g_strcmp0 (encoding_name, "RED")) {
2298           gchar *str;
2299 
2300           str = g_strdup_printf ("%u", pt);
2301           gst_sdp_media_add_format (media, str);
2302           g_free (str);
2303           str = g_strdup_printf ("%u red/%d", pt, clock_rate);
2304           *rtx_target_pt = pt;
2305           gst_sdp_media_add_attribute (media, "rtpmap", str);
2306           g_free (str);
2307         } else if (!g_strcmp0 (encoding_name, "ULPFEC")) {
2308           gchar *str;
2309 
2310           str = g_strdup_printf ("%u", pt);
2311           gst_sdp_media_add_format (media, str);
2312           g_free (str);
2313           str = g_strdup_printf ("%u ulpfec/%d", pt, clock_rate);
2314           gst_sdp_media_add_attribute (media, "rtpmap", str);
2315           g_free (str);
2316         }
2317       }
2318     }
2319   }
2320 }
2321 
2322 static void
_media_add_rtx(GstSDPMedia * media,WebRTCTransceiver * trans,GstCaps * offer_caps,gint target_pt,guint target_ssrc)2323 _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
2324     GstCaps * offer_caps, gint target_pt, guint target_ssrc)
2325 {
2326   guint i;
2327   const GstStructure *s;
2328 
2329   if (trans->local_rtx_ssrc_map)
2330     gst_structure_free (trans->local_rtx_ssrc_map);
2331 
2332   trans->local_rtx_ssrc_map =
2333       gst_structure_new_empty ("application/x-rtp-ssrc-map");
2334 
2335   for (i = 0; i < gst_caps_get_size (offer_caps); i++) {
2336     s = gst_caps_get_structure (offer_caps, i);
2337 
2338     if (gst_structure_has_name (s, "application/x-rtp")) {
2339       const gchar *encoding_name =
2340           gst_structure_get_string (s, "encoding-name");
2341       const gchar *apt_str = gst_structure_get_string (s, "apt");
2342       gint apt;
2343       gint clock_rate;
2344       gint pt;
2345 
2346       if (!apt_str)
2347         continue;
2348 
2349       apt = atoi (apt_str);
2350 
2351       if (gst_structure_get_int (s, "clock-rate", &clock_rate) &&
2352           gst_structure_get_int (s, "payload", &pt) && apt == target_pt) {
2353         if (!g_strcmp0 (encoding_name, "RTX")) {
2354           gchar *str;
2355 
2356           str = g_strdup_printf ("%u", pt);
2357           gst_sdp_media_add_format (media, str);
2358           g_free (str);
2359           str = g_strdup_printf ("%u rtx/%d", pt, clock_rate);
2360           gst_sdp_media_add_attribute (media, "rtpmap", str);
2361           g_free (str);
2362 
2363           str = g_strdup_printf ("%d apt=%d", pt, apt);
2364           gst_sdp_media_add_attribute (media, "fmtp", str);
2365           g_free (str);
2366 
2367           str = g_strdup_printf ("%u", target_ssrc);
2368           gst_structure_set (trans->local_rtx_ssrc_map, str, G_TYPE_UINT,
2369               g_random_int (), NULL);
2370         }
2371       }
2372     }
2373   }
2374 }
2375 
2376 static void
_get_rtx_target_pt_and_ssrc_from_caps(GstCaps * answer_caps,gint * target_pt,guint * target_ssrc)2377 _get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
2378     guint * target_ssrc)
2379 {
2380   const GstStructure *s = gst_caps_get_structure (answer_caps, 0);
2381 
2382   gst_structure_get_int (s, "payload", target_pt);
2383   gst_structure_get_uint (s, "ssrc", target_ssrc);
2384 }
2385 
2386 /* TODO: use the options argument */
2387 static GstSDPMessage *
_create_answer_task(GstWebRTCBin * webrtc,const GstStructure * options)2388 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
2389 {
2390   GstSDPMessage *ret = NULL;
2391   const GstWebRTCSessionDescription *pending_remote =
2392       webrtc->pending_remote_description;
2393   guint i;
2394   GStrv bundled = NULL;
2395   guint bundle_idx = 0;
2396   GString *bundled_mids = NULL;
2397   gchar *bundle_ufrag = NULL;
2398   gchar *bundle_pwd = NULL;
2399 
2400   if (!webrtc->pending_remote_description) {
2401     GST_ERROR_OBJECT (webrtc,
2402         "Asked to create an answer without a remote description");
2403     return NULL;
2404   }
2405 
2406   if (!_parse_bundle (pending_remote->sdp, &bundled))
2407     goto out;
2408 
2409   if (bundled) {
2410     if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) {
2411       GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
2412           bundled[0]);
2413       goto out;
2414     }
2415 
2416     if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) {
2417       bundled_mids = g_string_new ("BUNDLE");
2418     }
2419 
2420     _generate_ice_credentials (&bundle_ufrag, &bundle_pwd);
2421   }
2422 
2423   gst_sdp_message_new (&ret);
2424 
2425   /* FIXME: session id and version need special handling depending on the state we're in */
2426   gst_sdp_message_set_version (ret, "0");
2427   {
2428     const GstSDPOrigin *offer_origin =
2429         gst_sdp_message_get_origin (pending_remote->sdp);
2430     gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id, "0", "IN",
2431         "IP4", "0.0.0.0");
2432   }
2433   gst_sdp_message_set_session_name (ret, "-");
2434 
2435   for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
2436     const GstSDPAttribute *attr =
2437         gst_sdp_message_get_attribute (pending_remote->sdp, i);
2438 
2439     if (g_strcmp0 (attr->key, "ice-options") == 0) {
2440       gst_sdp_message_add_attribute (ret, attr->key, attr->value);
2441     }
2442   }
2443 
2444   for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
2445     GstSDPMedia *media = NULL;
2446     GstSDPMedia *offer_media;
2447     GstWebRTCRTPTransceiver *rtp_trans = NULL;
2448     WebRTCTransceiver *trans = NULL;
2449     GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
2450     GstWebRTCDTLSSetup offer_setup, answer_setup;
2451     GstCaps *offer_caps, *answer_caps = NULL;
2452     guint j;
2453     guint k;
2454     gint target_pt = -1;
2455     gint original_target_pt = -1;
2456     guint target_ssrc = 0;
2457     gboolean bundle_only;
2458 
2459     offer_media =
2460         (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
2461     bundle_only = _media_has_attribute_key (offer_media, "bundle-only");
2462 
2463     gst_sdp_media_new (&media);
2464     if (bundle_only && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE)
2465       gst_sdp_media_set_port_info (media, 0, 0);
2466     else
2467       gst_sdp_media_set_port_info (media, 9, 0);
2468     gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
2469 
2470     {
2471       /* FIXME: only needed when restarting ICE */
2472       gchar *ufrag, *pwd;
2473       if (!bundled) {
2474         _generate_ice_credentials (&ufrag, &pwd);
2475       } else {
2476         ufrag = g_strdup (bundle_ufrag);
2477         pwd = g_strdup (bundle_pwd);
2478       }
2479       gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
2480       gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
2481       g_free (ufrag);
2482       g_free (pwd);
2483     }
2484 
2485     for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
2486       const GstSDPAttribute *attr =
2487           gst_sdp_media_get_attribute (offer_media, j);
2488 
2489       if (g_strcmp0 (attr->key, "mid") == 0
2490           || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
2491         gst_sdp_media_add_attribute (media, attr->key, attr->value);
2492         /* FIXME: handle anything we want to keep */
2493       }
2494     }
2495 
2496     /* set the a=setup: attribute */
2497     offer_setup = _get_dtls_setup_from_media (offer_media);
2498     answer_setup = _intersect_dtls_setup (offer_setup);
2499     if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
2500       GST_WARNING_OBJECT (webrtc, "Could not intersect offer setup with "
2501           "transceiver direction");
2502       goto rejected;
2503     }
2504     _media_replace_setup (media, answer_setup);
2505 
2506     if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "application") == 0) {
2507       int sctp_port;
2508 
2509       if (gst_sdp_media_formats_len (offer_media) != 1) {
2510         GST_WARNING_OBJECT (webrtc, "Could not find a format in the m= line "
2511             "for webrtc-datachannel");
2512         goto rejected;
2513       }
2514       if (g_strcmp0 (gst_sdp_media_get_format (offer_media, 0),
2515               "webrtc-datachannel") != 0) {
2516         GST_WARNING_OBJECT (webrtc,
2517             "format field of data channel m= line "
2518             "is not \'webrtc-datachannel\'");
2519         goto rejected;
2520       }
2521       sctp_port = _get_sctp_port_from_media (offer_media);
2522       if (sctp_port == -1) {
2523         GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port");
2524         goto rejected;
2525       }
2526 
2527       /* XXX: older browsers will produce a different SDP format for data
2528        * channel that is currently not parsed correctly */
2529       gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP");
2530 
2531       gst_sdp_media_set_media (media, "application");
2532       gst_sdp_media_set_port_info (media, 9, 0);
2533       gst_sdp_media_add_format (media, "webrtc-datachannel");
2534 
2535       /* FIXME: negotiate this properly on renegotiation */
2536       gst_sdp_media_add_attribute (media, "sctp-port", "5000");
2537 
2538       _get_or_create_data_channel_transports (webrtc,
2539           bundled_mids ? bundle_idx : i);
2540 
2541       if (bundled_mids) {
2542         const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2543 
2544         g_assert (mid);
2545         g_string_append_printf (bundled_mids, " %s", mid);
2546       }
2547 
2548       _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport,
2549           media);
2550     } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0
2551         || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) {
2552       gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
2553 
2554       offer_caps = gst_caps_new_empty ();
2555       for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) {
2556         guint pt = atoi (gst_sdp_media_get_format (offer_media, j));
2557         GstCaps *caps;
2558 
2559         caps = gst_sdp_media_get_caps_from_media (offer_media, pt);
2560 
2561         /* gst_sdp_media_get_caps_from_media() produces caps with name
2562          * "application/x-unknown" which will fail intersection with
2563          * "application/x-rtp" caps so mangle the returns caps to have the
2564          * correct name here */
2565         for (k = 0; k < gst_caps_get_size (caps); k++) {
2566           GstStructure *s = gst_caps_get_structure (caps, k);
2567           gst_structure_set_name (s, "application/x-rtp");
2568         }
2569 
2570         gst_caps_append (offer_caps, caps);
2571       }
2572 
2573       for (j = 0; j < webrtc->priv->transceivers->len; j++) {
2574         GstCaps *trans_caps;
2575 
2576         rtp_trans =
2577             g_array_index (webrtc->priv->transceivers,
2578             GstWebRTCRTPTransceiver *, j);
2579         trans_caps =
2580             _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j);
2581 
2582         GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
2583             " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
2584 
2585         /* FIXME: technically this is a little overreaching as some fields we
2586          * we can deal with not having and/or we may have unrecognized fields
2587          * that we cannot actually support */
2588         if (trans_caps) {
2589           answer_caps = gst_caps_intersect (offer_caps, trans_caps);
2590           if (answer_caps && !gst_caps_is_empty (answer_caps)) {
2591             GST_LOG_OBJECT (webrtc,
2592                 "found compatible transceiver %" GST_PTR_FORMAT
2593                 " for offer media %u", trans, i);
2594             if (trans_caps)
2595               gst_caps_unref (trans_caps);
2596             break;
2597           } else {
2598             if (answer_caps) {
2599               gst_caps_unref (answer_caps);
2600               answer_caps = NULL;
2601             }
2602             if (trans_caps)
2603               gst_caps_unref (trans_caps);
2604             rtp_trans = NULL;
2605           }
2606         } else {
2607           rtp_trans = NULL;
2608         }
2609       }
2610 
2611       if (rtp_trans) {
2612         answer_dir = rtp_trans->direction;
2613         g_assert (answer_caps != NULL);
2614       } else {
2615         /* if no transceiver, then we only receive that stream and respond with
2616          * the exact same caps */
2617         /* FIXME: how to validate that subsequent elements can actually receive
2618          * this payload/format */
2619         answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
2620         answer_caps = gst_caps_ref (offer_caps);
2621       }
2622 
2623       if (!rtp_trans) {
2624         trans = _create_webrtc_transceiver (webrtc, answer_dir, i);
2625         rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2626       } else {
2627         trans = WEBRTC_TRANSCEIVER (rtp_trans);
2628       }
2629 
2630       if (!trans->do_nack) {
2631         answer_caps = gst_caps_make_writable (answer_caps);
2632         for (k = 0; k < gst_caps_get_size (answer_caps); k++) {
2633           GstStructure *s = gst_caps_get_structure (answer_caps, k);
2634           gst_structure_remove_fields (s, "rtcp-fb-nack", NULL);
2635         }
2636       }
2637 
2638       gst_sdp_media_set_media_from_caps (answer_caps, media);
2639 
2640       _get_rtx_target_pt_and_ssrc_from_caps (answer_caps, &target_pt,
2641           &target_ssrc);
2642 
2643       original_target_pt = target_pt;
2644 
2645       _media_add_fec (media, trans, offer_caps, &target_pt);
2646       if (trans->do_nack) {
2647         _media_add_rtx (media, trans, offer_caps, target_pt, target_ssrc);
2648         if (target_pt != original_target_pt)
2649           _media_add_rtx (media, trans, offer_caps, original_target_pt,
2650               target_ssrc);
2651       }
2652 
2653       if (answer_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)
2654         _media_add_ssrcs (media, answer_caps, webrtc,
2655             WEBRTC_TRANSCEIVER (rtp_trans));
2656 
2657       gst_caps_unref (answer_caps);
2658       answer_caps = NULL;
2659 
2660       /* set the new media direction */
2661       offer_dir = _get_direction_from_media (offer_media);
2662       answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
2663       if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
2664         GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
2665             "transceiver direction");
2666         goto rejected;
2667       }
2668       _media_replace_direction (media, answer_dir);
2669 
2670       if (!trans->stream) {
2671         TransportStream *item;
2672 
2673         if (bundled_mids) {
2674           const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid");
2675           item = _get_or_create_transport_stream (webrtc, bundle_idx, FALSE);
2676 
2677           g_assert (mid);
2678           g_string_append_printf (bundled_mids, " %s", mid);
2679         } else {
2680           item = _get_or_create_transport_stream (webrtc, i, FALSE);
2681         }
2682         webrtc_transceiver_set_transport (trans, item);
2683       }
2684 
2685       /* set the a=fingerprint: for this transport */
2686       _add_fingerprint_to_media (trans->stream->transport, media);
2687 
2688       gst_caps_unref (offer_caps);
2689     } else {
2690       GST_WARNING_OBJECT (webrtc, "unknown m= line media name");
2691       goto rejected;
2692     }
2693 
2694     if (0) {
2695     rejected:
2696       GST_INFO_OBJECT (webrtc, "media %u rejected", i);
2697       gst_sdp_media_free (media);
2698       gst_sdp_media_copy (offer_media, &media);
2699       gst_sdp_media_set_port_info (media, 0, 0);
2700     }
2701     gst_sdp_message_add_media (ret, media);
2702     gst_sdp_media_free (media);
2703   }
2704 
2705   if (bundled_mids) {
2706     gchar *mids = g_string_free (bundled_mids, FALSE);
2707 
2708     gst_sdp_message_add_attribute (ret, "group", mids);
2709     g_free (mids);
2710   }
2711 
2712   if (bundle_ufrag)
2713     g_free (bundle_ufrag);
2714 
2715   if (bundle_pwd)
2716     g_free (bundle_pwd);
2717 
2718   /* FIXME: can we add not matched transceivers? */
2719 
2720   /* XXX: only true for the initial offerer */
2721   g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
2722 
2723 out:
2724   if (bundled)
2725     g_strfreev (bundled);
2726 
2727   return ret;
2728 }
2729 
2730 struct create_sdp
2731 {
2732   GstStructure *options;
2733   GstPromise *promise;
2734   GstWebRTCSDPType type;
2735 };
2736 
2737 static void
_create_sdp_task(GstWebRTCBin * webrtc,struct create_sdp * data)2738 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
2739 {
2740   GstWebRTCSessionDescription *desc = NULL;
2741   GstSDPMessage *sdp = NULL;
2742   GstStructure *s = NULL;
2743 
2744   GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
2745       gst_webrtc_sdp_type_to_string (data->type), data->options);
2746 
2747   if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
2748     sdp = _create_offer_task (webrtc, data->options);
2749   else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
2750     sdp = _create_answer_task (webrtc, data->options);
2751   else {
2752     g_assert_not_reached ();
2753     goto out;
2754   }
2755 
2756   if (sdp) {
2757     desc = gst_webrtc_session_description_new (data->type, sdp);
2758     s = gst_structure_new ("application/x-gst-promise",
2759         gst_webrtc_sdp_type_to_string (data->type),
2760         GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
2761   }
2762 
2763 out:
2764   PC_UNLOCK (webrtc);
2765   gst_promise_reply (data->promise, s);
2766   PC_LOCK (webrtc);
2767 
2768   if (desc)
2769     gst_webrtc_session_description_free (desc);
2770 }
2771 
2772 static void
_free_create_sdp_data(struct create_sdp * data)2773 _free_create_sdp_data (struct create_sdp *data)
2774 {
2775   if (data->options)
2776     gst_structure_free (data->options);
2777   gst_promise_unref (data->promise);
2778   g_free (data);
2779 }
2780 
2781 static void
gst_webrtc_bin_create_offer(GstWebRTCBin * webrtc,const GstStructure * options,GstPromise * promise)2782 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
2783     const GstStructure * options, GstPromise * promise)
2784 {
2785   struct create_sdp *data = g_new0 (struct create_sdp, 1);
2786 
2787   if (options)
2788     data->options = gst_structure_copy (options);
2789   data->promise = gst_promise_ref (promise);
2790   data->type = GST_WEBRTC_SDP_TYPE_OFFER;
2791 
2792   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
2793       data, (GDestroyNotify) _free_create_sdp_data);
2794 }
2795 
2796 static void
gst_webrtc_bin_create_answer(GstWebRTCBin * webrtc,const GstStructure * options,GstPromise * promise)2797 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
2798     const GstStructure * options, GstPromise * promise)
2799 {
2800   struct create_sdp *data = g_new0 (struct create_sdp, 1);
2801 
2802   if (options)
2803     data->options = gst_structure_copy (options);
2804   data->promise = gst_promise_ref (promise);
2805   data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
2806 
2807   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
2808       data, (GDestroyNotify) _free_create_sdp_data);
2809 }
2810 
2811 static GstWebRTCBinPad *
_create_pad_for_sdp_media(GstWebRTCBin * webrtc,GstPadDirection direction,guint media_idx)2812 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
2813     guint media_idx)
2814 {
2815   GstWebRTCBinPad *pad;
2816   gchar *pad_name;
2817 
2818   pad_name =
2819       g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
2820       media_idx);
2821   pad = gst_webrtc_bin_pad_new (pad_name, direction);
2822   g_free (pad_name);
2823   pad->mlineindex = media_idx;
2824 
2825   return pad;
2826 }
2827 
2828 static GstWebRTCRTPTransceiver *
_find_transceiver_for_sdp_media(GstWebRTCBin * webrtc,const GstSDPMessage * sdp,guint media_idx)2829 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
2830     const GstSDPMessage * sdp, guint media_idx)
2831 {
2832   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
2833   GstWebRTCRTPTransceiver *ret = NULL;
2834   int i;
2835 
2836   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
2837     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
2838 
2839     if (g_strcmp0 (attr->key, "mid") == 0) {
2840       if ((ret =
2841               _find_transceiver (webrtc, attr->value,
2842                   (FindTransceiverFunc) match_for_mid)))
2843         goto out;
2844     }
2845   }
2846 
2847   ret = _find_transceiver (webrtc, &media_idx,
2848       (FindTransceiverFunc) transceiver_match_for_mline);
2849 
2850 out:
2851   GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
2852   return ret;
2853 }
2854 
2855 static GstPad *
_connect_input_stream(GstWebRTCBin * webrtc,GstWebRTCBinPad * pad)2856 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
2857 {
2858 /*
2859  * Not-bundle case:
2860  *
2861  * ,-------------------------webrtcbin-------------------------,
2862  * ;                                                           ;
2863  * ;          ,-------rtpbin-------,   ,--transport_send_%u--, ;
2864  * ;          ;    send_rtp_src_%u o---o rtp_sink            ; ;
2865  * ;          ;                    ;   ;                     ; ;
2866  * ;          ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
2867  * ; sink_%u  ;                    ;   '---------------------' ;
2868  * o----------o send_rtp_sink_%u   ;                           ;
2869  * ;          '--------------------'                           ;
2870  * '--------------------- -------------------------------------'
2871  */
2872 
2873 /*
2874  * Bundle case:
2875  * ,--------------------------------webrtcbin--------------------------------,
2876  * ;                                                                         ;
2877  * ;                        ,-------rtpbin-------,   ,--transport_send_%u--, ;
2878  * ;                        ;    send_rtp_src_%u o---o rtp_sink            ; ;
2879  * ;                        ;                    ;   ;                     ; ;
2880  * ;                        ;   send_rtcp_src_%u o---o rtcp_sink           ; ;
2881  * ; sink_%u ,---funnel---, ;                    ;   '---------------------' ;
2882  * o---------o sink_%u    ; ;                    ;                           ;
2883  * ; sink_%u ;        src o-o send_rtp_sink_%u   ;                           ;
2884  * o---------o sink_%u    ; ;                    ;                           ;
2885  * ;         '------------' '--------------------'                           ;
2886  * '-------------------------------------------------------------------------'
2887  */
2888   GstPadTemplate *rtp_templ;
2889   GstPad *rtp_sink;
2890   gchar *pad_name;
2891   WebRTCTransceiver *trans;
2892 
2893   g_return_val_if_fail (pad->trans != NULL, NULL);
2894 
2895   GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
2896 
2897   trans = WEBRTC_TRANSCEIVER (pad->trans);
2898 
2899   g_assert (trans->stream);
2900 
2901   if (!webrtc->rtpfunnel) {
2902     rtp_templ =
2903         _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
2904         "send_rtp_sink_%u");
2905     g_assert (rtp_templ);
2906 
2907     pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
2908     rtp_sink =
2909         gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
2910     g_free (pad_name);
2911     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
2912     gst_object_unref (rtp_sink);
2913 
2914     pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
2915     if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
2916             GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
2917       g_warn_if_reached ();
2918     g_free (pad_name);
2919   } else {
2920     gchar *pad_name = g_strdup_printf ("sink_%u", pad->mlineindex);
2921     GstPad *funnel_sinkpad =
2922         gst_element_get_request_pad (webrtc->rtpfunnel, pad_name);
2923 
2924     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), funnel_sinkpad);
2925 
2926     g_free (pad_name);
2927     gst_object_unref (funnel_sinkpad);
2928   }
2929 
2930   gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
2931 
2932   return GST_PAD (pad);
2933 }
2934 
2935 /* output pads are receiving elements */
2936 static void
_connect_output_stream(GstWebRTCBin * webrtc,TransportStream * stream,guint session_id)2937 _connect_output_stream (GstWebRTCBin * webrtc,
2938     TransportStream * stream, guint session_id)
2939 {
2940 /*
2941  * ,------------------------webrtcbin------------------------,
2942  * ;                             ,---------rtpbin---------,  ;
2943  * ; ,-transport_receive_%u--,   ;                        ;  ;
2944  * ; ;               rtp_src o---o recv_rtp_sink_%u       ;  ;
2945  * ; ;                       ;   ;                        ;  ;
2946  * ; ;              rtcp_src o---o recv_rtcp_sink_%u      ;  ;
2947  * ; '-----------------------'   ;                        ;  ; src_%u
2948  * ;                             ;  recv_rtp_src_%u_%u_%u o--o
2949  * ;                             '------------------------'  ;
2950  * '---------------------------------------------------------'
2951  */
2952   gchar *pad_name;
2953 
2954   GST_INFO_OBJECT (webrtc, "linking output stream %u", session_id);
2955 
2956   pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id);
2957   if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin),
2958           "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
2959     g_warn_if_reached ();
2960   g_free (pad_name);
2961 
2962   gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin));
2963 
2964   /* The webrtcbin src_%u output pads will be created when rtpbin receives
2965    * data on that stream in on_rtpbin_pad_added() */
2966 }
2967 
2968 typedef struct
2969 {
2970   guint mlineindex;
2971   gchar *candidate;
2972 } IceCandidateItem;
2973 
2974 static void
_clear_ice_candidate_item(IceCandidateItem ** item)2975 _clear_ice_candidate_item (IceCandidateItem ** item)
2976 {
2977   g_free ((*item)->candidate);
2978   g_free (*item);
2979 }
2980 
2981 static void
_add_ice_candidate(GstWebRTCBin * webrtc,IceCandidateItem * item)2982 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item)
2983 {
2984   GstWebRTCICEStream *stream;
2985 
2986   stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
2987   if (stream == NULL) {
2988     GST_WARNING_OBJECT (webrtc, "Unknown mline %u, ignoring", item->mlineindex);
2989     return;
2990   }
2991 
2992   GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
2993       item->mlineindex, item->candidate);
2994 
2995   gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
2996 }
2997 
2998 static gboolean
_filter_sdp_fields(GQuark field_id,const GValue * value,GstStructure * new_structure)2999 _filter_sdp_fields (GQuark field_id, const GValue * value,
3000     GstStructure * new_structure)
3001 {
3002   if (!g_str_has_prefix (g_quark_to_string (field_id), "a-")) {
3003     gst_structure_id_set_value (new_structure, field_id, value);
3004   }
3005   return TRUE;
3006 }
3007 
3008 static void
_update_transport_ptmap_from_media(GstWebRTCBin * webrtc,TransportStream * stream,const GstSDPMessage * sdp,guint media_idx)3009 _update_transport_ptmap_from_media (GstWebRTCBin * webrtc,
3010     TransportStream * stream, const GstSDPMessage * sdp, guint media_idx)
3011 {
3012   guint i, len;
3013   const gchar *proto;
3014   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3015 
3016   /* get proto */
3017   proto = gst_sdp_media_get_proto (media);
3018   if (proto != NULL) {
3019     /* Parse global SDP attributes once */
3020     GstCaps *global_caps = gst_caps_new_empty_simple ("application/x-unknown");
3021     GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
3022     gst_sdp_message_attributes_to_caps (sdp, global_caps);
3023     GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
3024     gst_sdp_media_attributes_to_caps (media, global_caps);
3025 
3026     len = gst_sdp_media_formats_len (media);
3027     for (i = 0; i < len; i++) {
3028       GstCaps *caps, *outcaps;
3029       GstStructure *s;
3030       PtMapItem item;
3031       gint pt;
3032       guint j;
3033 
3034       pt = atoi (gst_sdp_media_get_format (media, i));
3035 
3036       GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
3037 
3038       /* convert caps */
3039       caps = gst_sdp_media_get_caps_from_media (media, pt);
3040       if (caps == NULL) {
3041         GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
3042         continue;
3043       }
3044 
3045       /* Merge in global caps */
3046       /* Intersect will merge in missing fields to the current caps */
3047       outcaps = gst_caps_intersect (caps, global_caps);
3048       gst_caps_unref (caps);
3049 
3050       s = gst_caps_get_structure (outcaps, 0);
3051       gst_structure_set_name (s, "application/x-rtp");
3052       if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "ULPFEC"))
3053         gst_structure_set (s, "is-fec", G_TYPE_BOOLEAN, TRUE, NULL);
3054 
3055       item.caps = gst_caps_new_empty ();
3056 
3057       for (j = 0; j < gst_caps_get_size (outcaps); j++) {
3058         GstStructure *s = gst_caps_get_structure (outcaps, j);
3059         GstStructure *filtered =
3060             gst_structure_new_empty (gst_structure_get_name (s));
3061 
3062         gst_structure_foreach (s,
3063             (GstStructureForeachFunc) _filter_sdp_fields, filtered);
3064         gst_caps_append_structure (item.caps, filtered);
3065       }
3066 
3067       item.pt = pt;
3068       gst_caps_unref (outcaps);
3069 
3070       g_array_append_val (stream->ptmap, item);
3071     }
3072 
3073     gst_caps_unref (global_caps);
3074   }
3075 }
3076 
3077 static void
_update_transceiver_from_sdp_media(GstWebRTCBin * webrtc,const GstSDPMessage * sdp,guint media_idx,TransportStream * stream,GstWebRTCRTPTransceiver * rtp_trans,GStrv bundled,guint bundle_idx,gboolean * should_connect_bundle_stream)3078 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
3079     const GstSDPMessage * sdp, guint media_idx,
3080     TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans,
3081     GStrv bundled, guint bundle_idx, gboolean * should_connect_bundle_stream)
3082 {
3083   WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
3084   GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
3085   GstWebRTCRTPTransceiverDirection new_dir;
3086   const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
3087   GstWebRTCDTLSSetup new_setup;
3088   gboolean new_rtcp_mux, new_rtcp_rsize;
3089   int i;
3090 
3091   rtp_trans->mline = media_idx;
3092 
3093   for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
3094     const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
3095 
3096     if (g_strcmp0 (attr->key, "mid") == 0) {
3097       g_free (rtp_trans->mid);
3098       rtp_trans->mid = g_strdup (attr->value);
3099     }
3100   }
3101 
3102   {
3103     const GstSDPMedia *local_media, *remote_media;
3104     GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
3105     GstWebRTCDTLSSetup local_setup, remote_setup;
3106 
3107     local_media =
3108         gst_sdp_message_get_media (webrtc->current_local_description->sdp,
3109         media_idx);
3110     remote_media =
3111         gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
3112         media_idx);
3113 
3114     local_setup = _get_dtls_setup_from_media (local_media);
3115     remote_setup = _get_dtls_setup_from_media (remote_media);
3116     new_setup = _get_final_setup (local_setup, remote_setup);
3117     if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
3118       return;
3119 
3120     local_dir = _get_direction_from_media (local_media);
3121     remote_dir = _get_direction_from_media (remote_media);
3122     new_dir = _get_final_direction (local_dir, remote_dir);
3123 
3124     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
3125       return;
3126 
3127     if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
3128         && prev_dir != new_dir) {
3129       GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
3130       return;
3131     }
3132 
3133     if (!bundled || bundle_idx == media_idx) {
3134       new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
3135           && _media_has_attribute_key (remote_media, "rtcp-mux");
3136       new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
3137           && _media_has_attribute_key (remote_media, "rtcp-rsize");
3138 
3139       {
3140         GObject *session;
3141         g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
3142             media_idx, &session);
3143         if (session) {
3144           g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
3145           g_object_unref (session);
3146         }
3147       }
3148 
3149       g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
3150     }
3151   }
3152 
3153   if (new_dir != prev_dir) {
3154     ReceiveState receive_state = 0;
3155 
3156     GST_TRACE_OBJECT (webrtc, "transceiver direction change");
3157 
3158     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
3159         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
3160       GstWebRTCBinPad *pad =
3161           _find_pad_for_mline (webrtc, GST_PAD_SINK, media_idx);
3162       if (pad) {
3163         GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
3164             " for transceiver %" GST_PTR_FORMAT, pad, trans);
3165         g_assert (pad->trans == rtp_trans);
3166         g_assert (pad->mlineindex == media_idx);
3167         gst_object_unref (pad);
3168       } else {
3169         GST_DEBUG_OBJECT (webrtc,
3170             "creating new send pad for transceiver %" GST_PTR_FORMAT, trans);
3171         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, media_idx);
3172         pad->trans = gst_object_ref (rtp_trans);
3173         _connect_input_stream (webrtc, pad);
3174         _add_pad (webrtc, pad);
3175       }
3176     }
3177     if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
3178         new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
3179       GstWebRTCBinPad *pad =
3180           _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
3181       if (pad) {
3182         GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
3183             " for transceiver %" GST_PTR_FORMAT, pad, trans);
3184         g_assert (pad->trans == rtp_trans);
3185         g_assert (pad->mlineindex == media_idx);
3186         gst_object_unref (pad);
3187       } else {
3188         GST_DEBUG_OBJECT (webrtc,
3189             "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
3190         pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
3191         pad->trans = gst_object_ref (rtp_trans);
3192 
3193         if (!trans->stream) {
3194           TransportStream *item;
3195 
3196           item =
3197               _get_or_create_transport_stream (webrtc,
3198               bundled ? bundle_idx : media_idx, FALSE);
3199           webrtc_transceiver_set_transport (trans, item);
3200         }
3201 
3202         if (!bundled)
3203           _connect_output_stream (webrtc, trans->stream, media_idx);
3204         else
3205           *should_connect_bundle_stream = TRUE;
3206         /* delay adding the pad until rtpbin creates the recv output pad
3207          * to ghost to so queries/events travel through the pipeline correctly
3208          * as soon as the pad is added */
3209         _add_pad_to_list (webrtc, pad);
3210       }
3211 
3212       receive_state = RECEIVE_STATE_PASS;
3213     } else if (!bundled) {
3214       receive_state = RECEIVE_STATE_DROP;
3215     }
3216 
3217     if (!bundled || bundle_idx == media_idx)
3218       g_object_set (stream, "dtls-client",
3219           new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
3220 
3221     /* Must be after setting the "dtls-client" so that data is not pushed into
3222      * the dtlssrtp elements before the ssl direction has been set which will
3223      * throw SSL errors */
3224     if (receive_state > 0)
3225       transport_receive_bin_set_receive_state (stream->receive_bin,
3226           receive_state);
3227 
3228     rtp_trans->mline = media_idx;
3229     rtp_trans->current_direction = new_dir;
3230   }
3231 }
3232 
3233 /* must be called with the pc lock held */
3234 static gint
_generate_data_channel_id(GstWebRTCBin * webrtc)3235 _generate_data_channel_id (GstWebRTCBin * webrtc)
3236 {
3237   gboolean is_client;
3238   gint new_id = -1, max_channels = 0;
3239 
3240   if (webrtc->priv->sctp_transport) {
3241     g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
3242         NULL);
3243   }
3244   if (max_channels <= 0) {
3245     max_channels = 65534;
3246   }
3247 
3248   g_object_get (webrtc->priv->sctp_transport->transport, "client", &is_client,
3249       NULL);
3250 
3251   /* TODO: a better search algorithm */
3252   do {
3253     GstWebRTCDataChannel *channel;
3254 
3255     new_id++;
3256 
3257     if (new_id < 0 || new_id >= max_channels) {
3258       /* exhausted id space */
3259       GST_WARNING_OBJECT (webrtc, "Could not find a suitable "
3260           "data channel id (max %i)", max_channels);
3261       return -1;
3262     }
3263 
3264     /* client must generate even ids, server must generate odd ids */
3265     if (new_id % 2 == ! !is_client)
3266       continue;
3267 
3268     channel = _find_data_channel_for_id (webrtc, new_id);
3269     if (!channel)
3270       break;
3271   } while (TRUE);
3272 
3273   return new_id;
3274 }
3275 
3276 static void
_update_data_channel_from_sdp_media(GstWebRTCBin * webrtc,const GstSDPMessage * sdp,guint media_idx,TransportStream * stream)3277 _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc,
3278     const GstSDPMessage * sdp, guint media_idx, TransportStream * stream)
3279 {
3280   const GstSDPMedia *local_media, *remote_media;
3281   GstWebRTCDTLSSetup local_setup, remote_setup, new_setup;
3282   TransportReceiveBin *receive;
3283   int local_port, remote_port;
3284   guint64 local_max_size, remote_max_size, max_size;
3285   int i;
3286 
3287   local_media =
3288       gst_sdp_message_get_media (webrtc->current_local_description->sdp,
3289       media_idx);
3290   remote_media =
3291       gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
3292       media_idx);
3293 
3294   local_setup = _get_dtls_setup_from_media (local_media);
3295   remote_setup = _get_dtls_setup_from_media (remote_media);
3296   new_setup = _get_final_setup (local_setup, remote_setup);
3297   if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
3298     return;
3299 
3300   /* data channel is always rtcp-muxed to avoid generating ICE candidates
3301    * for RTCP */
3302   g_object_set (stream, "rtcp-mux", TRUE, "dtls-client",
3303       new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
3304 
3305   local_port = _get_sctp_port_from_media (local_media);
3306   remote_port = _get_sctp_port_from_media (local_media);
3307   if (local_port == -1 || remote_port == -1)
3308     return;
3309 
3310   if (0 == (local_max_size =
3311           _get_sctp_max_message_size_from_media (local_media)))
3312     local_max_size = G_MAXUINT64;
3313   if (0 == (remote_max_size =
3314           _get_sctp_max_message_size_from_media (remote_media)))
3315     remote_max_size = G_MAXUINT64;
3316   max_size = MIN (local_max_size, remote_max_size);
3317 
3318   webrtc->priv->sctp_transport->max_message_size = max_size;
3319 
3320   g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port",
3321       local_port, NULL);
3322   g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port",
3323       remote_port, NULL);
3324 
3325   for (i = 0; i < webrtc->priv->data_channels->len; i++) {
3326     GstWebRTCDataChannel *channel;
3327 
3328     channel =
3329         g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, i);
3330 
3331     if (channel->id == -1)
3332       channel->id = _generate_data_channel_id (webrtc);
3333     if (channel->id == -1)
3334       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
3335           ("%s", "Failed to generate an identifier for a data channel"), NULL);
3336 
3337     if (webrtc->priv->sctp_transport->association_established
3338         && !channel->negotiated && !channel->opened) {
3339       _link_data_channel_to_sctp (webrtc, channel);
3340       gst_webrtc_data_channel_start_negotiation (channel);
3341     }
3342   }
3343 
3344   receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
3345   transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
3346 }
3347 
3348 static gboolean
_find_compatible_unassociated_transceiver(GstWebRTCRTPTransceiver * p1,gconstpointer data)3349 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
3350     gconstpointer data)
3351 {
3352   if (p1->mid)
3353     return FALSE;
3354   if (p1->mline != -1)
3355     return FALSE;
3356 
3357   return TRUE;
3358 }
3359 
3360 static void
_connect_rtpfunnel(GstWebRTCBin * webrtc,guint session_id)3361 _connect_rtpfunnel (GstWebRTCBin * webrtc, guint session_id)
3362 {
3363   gchar *pad_name;
3364   GstPad *queue_srcpad;
3365   GstPad *rtp_sink;
3366   TransportStream *stream = _find_transport_for_session (webrtc, session_id);
3367   GstElement *queue;
3368 
3369   g_assert (stream);
3370 
3371   if (webrtc->rtpfunnel)
3372     goto done;
3373 
3374   webrtc->rtpfunnel = gst_element_factory_make ("rtpfunnel", NULL);
3375   gst_bin_add (GST_BIN (webrtc), webrtc->rtpfunnel);
3376   gst_element_sync_state_with_parent (webrtc->rtpfunnel);
3377 
3378   queue = gst_element_factory_make ("queue", NULL);
3379   gst_bin_add (GST_BIN (webrtc), queue);
3380   gst_element_sync_state_with_parent (queue);
3381 
3382   gst_element_link (webrtc->rtpfunnel, queue);
3383 
3384   queue_srcpad = gst_element_get_static_pad (queue, "src");
3385 
3386   pad_name = g_strdup_printf ("send_rtp_sink_%d", session_id);
3387   rtp_sink = gst_element_get_request_pad (webrtc->rtpbin, pad_name);
3388   g_free (pad_name);
3389   gst_pad_link (queue_srcpad, rtp_sink);
3390   gst_object_unref (queue_srcpad);
3391   gst_object_unref (rtp_sink);
3392 
3393   pad_name = g_strdup_printf ("send_rtp_src_%d", session_id);
3394   if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
3395           GST_ELEMENT (stream->send_bin), "rtp_sink"))
3396     g_warn_if_reached ();
3397   g_free (pad_name);
3398 
3399 done:
3400   return;
3401 }
3402 
3403 static gboolean
_update_transceivers_from_sdp(GstWebRTCBin * webrtc,SDPSource source,GstWebRTCSessionDescription * sdp)3404 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
3405     GstWebRTCSessionDescription * sdp)
3406 {
3407   int i;
3408   gboolean ret = FALSE;
3409   GStrv bundled = NULL;
3410   guint bundle_idx = 0;
3411   gboolean should_connect_bundle_stream = FALSE;
3412   TransportStream *bundle_stream = NULL;
3413 
3414   if (!_parse_bundle (sdp->sdp, &bundled))
3415     goto done;
3416 
3417   if (bundled) {
3418 
3419     if (!_get_bundle_index (sdp->sdp, bundled, &bundle_idx)) {
3420       GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
3421           bundled[0]);
3422       goto done;
3423     }
3424 
3425     bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx,
3426         _message_media_is_datachannel (sdp->sdp, bundle_idx));
3427 
3428     _connect_rtpfunnel (webrtc, bundle_idx);
3429 
3430     g_array_set_size (bundle_stream->ptmap, 0);
3431     for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
3432       _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i);
3433     }
3434   }
3435 
3436   for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
3437     const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
3438     TransportStream *stream;
3439     GstWebRTCRTPTransceiver *trans;
3440     guint transport_idx;
3441 
3442     /* skip rejected media */
3443     if (gst_sdp_media_get_port (media) == 0)
3444       continue;
3445 
3446     if (bundled)
3447       transport_idx = bundle_idx;
3448     else
3449       transport_idx = i;
3450 
3451     trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
3452 
3453     stream = _get_or_create_transport_stream (webrtc, transport_idx,
3454         _message_media_is_datachannel (sdp->sdp, transport_idx));
3455     if (!bundled) {
3456       g_array_set_size (stream->ptmap, 0);
3457       _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i);
3458     }
3459 
3460     if (trans)
3461       webrtc_transceiver_set_transport ((WebRTCTransceiver *) trans, stream);
3462 
3463     if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
3464       GST_ERROR ("State mismatch.  Could not find local transceiver by mline.");
3465       goto done;
3466     } else {
3467       if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 ||
3468           g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) {
3469         if (trans) {
3470           _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
3471               trans, bundled, bundle_idx, &should_connect_bundle_stream);
3472         } else {
3473           trans = _find_transceiver (webrtc, NULL,
3474               (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
3475           /* XXX: default to the advertised direction in the sdp for new
3476            * transceviers.  The spec doesn't actually say what happens here, only
3477            * that calls to setDirection will change the value.  Nothing about
3478            * a default value when the transceiver is created internally */
3479           if (!trans) {
3480             trans =
3481                 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
3482                     _get_direction_from_media (media), i));
3483           }
3484           _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream,
3485               trans, bundled, bundle_idx, &should_connect_bundle_stream);
3486         }
3487       } else if (_message_media_is_datachannel (sdp->sdp, i)) {
3488         _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream);
3489       } else {
3490         GST_ERROR_OBJECT (webrtc, "Unknown media type in SDP at index %u", i);
3491       }
3492     }
3493   }
3494 
3495   if (should_connect_bundle_stream) {
3496     g_assert (bundle_stream);
3497     _connect_output_stream (webrtc, bundle_stream, bundle_idx);
3498   }
3499 
3500   ret = TRUE;
3501 
3502 done:
3503   if (bundled)
3504     g_strfreev (bundled);
3505   return ret;
3506 }
3507 
3508 struct set_description
3509 {
3510   GstPromise *promise;
3511   SDPSource source;
3512   GstWebRTCSessionDescription *sdp;
3513 };
3514 
3515 /* http://w3c.github.io/webrtc-pc/#set-description */
3516 static void
_set_description_task(GstWebRTCBin * webrtc,struct set_description * sd)3517 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
3518 {
3519   GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
3520   GError *error = NULL;
3521   GStrv bundled = NULL;
3522   guint bundle_idx = 0;
3523   guint i;
3524 
3525   {
3526     gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
3527         webrtc->signaling_state);
3528     gchar *type_str =
3529         _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
3530     gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
3531     GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
3532         _sdp_source_to_string (sd->source), type_str, state);
3533     GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
3534     g_free (sdp_text);
3535     g_free (state);
3536     g_free (type_str);
3537   }
3538 
3539   if (!validate_sdp (webrtc->signaling_state, sd->source, sd->sdp, &error)) {
3540     GST_ERROR_OBJECT (webrtc, "%s", error->message);
3541     g_clear_error (&error);
3542     goto out;
3543   }
3544 
3545   if (webrtc->priv->is_closed) {
3546     GST_WARNING_OBJECT (webrtc, "we are closed");
3547     goto out;
3548   }
3549 
3550   if (!_parse_bundle (sd->sdp->sdp, &bundled))
3551     goto out;
3552 
3553   if (bundled) {
3554     if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) {
3555       GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching",
3556           bundled[0]);
3557       goto out;
3558     }
3559   }
3560 
3561   switch (sd->sdp->type) {
3562     case GST_WEBRTC_SDP_TYPE_OFFER:{
3563       if (sd->source == SDP_LOCAL) {
3564         if (webrtc->pending_local_description)
3565           gst_webrtc_session_description_free
3566               (webrtc->pending_local_description);
3567         webrtc->pending_local_description =
3568             gst_webrtc_session_description_copy (sd->sdp);
3569         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
3570       } else {
3571         if (webrtc->pending_remote_description)
3572           gst_webrtc_session_description_free
3573               (webrtc->pending_remote_description);
3574         webrtc->pending_remote_description =
3575             gst_webrtc_session_description_copy (sd->sdp);
3576         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
3577       }
3578       break;
3579     }
3580     case GST_WEBRTC_SDP_TYPE_ANSWER:{
3581       if (sd->source == SDP_LOCAL) {
3582         if (webrtc->current_local_description)
3583           gst_webrtc_session_description_free
3584               (webrtc->current_local_description);
3585         webrtc->current_local_description =
3586             gst_webrtc_session_description_copy (sd->sdp);
3587 
3588         if (webrtc->current_remote_description)
3589           gst_webrtc_session_description_free
3590               (webrtc->current_remote_description);
3591         webrtc->current_remote_description = webrtc->pending_remote_description;
3592         webrtc->pending_remote_description = NULL;
3593       } else {
3594         if (webrtc->current_remote_description)
3595           gst_webrtc_session_description_free
3596               (webrtc->current_remote_description);
3597         webrtc->current_remote_description =
3598             gst_webrtc_session_description_copy (sd->sdp);
3599 
3600         if (webrtc->current_local_description)
3601           gst_webrtc_session_description_free
3602               (webrtc->current_local_description);
3603         webrtc->current_local_description = webrtc->pending_local_description;
3604         webrtc->pending_local_description = NULL;
3605       }
3606 
3607       if (webrtc->pending_local_description)
3608         gst_webrtc_session_description_free (webrtc->pending_local_description);
3609       webrtc->pending_local_description = NULL;
3610 
3611       if (webrtc->pending_remote_description)
3612         gst_webrtc_session_description_free
3613             (webrtc->pending_remote_description);
3614       webrtc->pending_remote_description = NULL;
3615 
3616       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
3617       break;
3618     }
3619     case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
3620       GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
3621       if (sd->source == SDP_LOCAL) {
3622         if (webrtc->pending_local_description)
3623           gst_webrtc_session_description_free
3624               (webrtc->pending_local_description);
3625         webrtc->pending_local_description = NULL;
3626       } else {
3627         if (webrtc->pending_remote_description)
3628           gst_webrtc_session_description_free
3629               (webrtc->pending_remote_description);
3630         webrtc->pending_remote_description = NULL;
3631       }
3632 
3633       new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
3634       break;
3635     }
3636     case GST_WEBRTC_SDP_TYPE_PRANSWER:{
3637       GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
3638       if (sd->source == SDP_LOCAL) {
3639         if (webrtc->pending_local_description)
3640           gst_webrtc_session_description_free
3641               (webrtc->pending_local_description);
3642         webrtc->pending_local_description =
3643             gst_webrtc_session_description_copy (sd->sdp);
3644 
3645         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
3646       } else {
3647         if (webrtc->pending_remote_description)
3648           gst_webrtc_session_description_free
3649               (webrtc->pending_remote_description);
3650         webrtc->pending_remote_description =
3651             gst_webrtc_session_description_copy (sd->sdp);
3652 
3653         new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
3654       }
3655       break;
3656     }
3657   }
3658 
3659   if (new_signaling_state != webrtc->signaling_state) {
3660     gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
3661         webrtc->signaling_state);
3662     gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
3663         new_signaling_state);
3664     GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
3665         "to %s", from, to);
3666     webrtc->signaling_state = new_signaling_state;
3667     PC_UNLOCK (webrtc);
3668     g_object_notify (G_OBJECT (webrtc), "signaling-state");
3669     PC_LOCK (webrtc);
3670 
3671     g_free (from);
3672     g_free (to);
3673   }
3674 
3675   if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
3676     /* FIXME:
3677      * If the mid value of an RTCRtpTransceiver was set to a non-null value
3678      * by the RTCSessionDescription that is being rolled back, set the mid
3679      * value of that transceiver to null, as described by [JSEP]
3680      * (section 4.1.7.2.).
3681      * If an RTCRtpTransceiver was created by applying the
3682      * RTCSessionDescription that is being rolled back, and a track has not
3683      * been attached to it via addTrack, remove that transceiver from
3684      * connection's set of transceivers, as described by [JSEP]
3685      * (section 4.1.7.2.).
3686      * Restore the value of connection's [[ sctpTransport]] internal slot
3687      * to its value at the last stable signaling state.
3688      */
3689   }
3690 
3691   if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
3692     gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
3693     GList *tmp;
3694 
3695     /* media modifications */
3696     _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp);
3697 
3698     for (tmp = webrtc->priv->pending_sink_transceivers; tmp; tmp = tmp->next) {
3699       GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data);
3700       const GstSDPMedia *media;
3701 
3702       media = gst_sdp_message_get_media (sd->sdp->sdp, pad->mlineindex);
3703       /* skip rejected media */
3704       /* FIXME: arrange for an appropriate flow return */
3705       if (gst_sdp_media_get_port (media) == 0)
3706         continue;
3707 
3708       _connect_input_stream (webrtc, pad);
3709       gst_pad_remove_probe (GST_PAD (pad), pad->block_id);
3710       pad->block_id = 0;
3711     }
3712 
3713     g_list_free_full (webrtc->priv->pending_sink_transceivers,
3714         (GDestroyNotify) gst_object_unref);
3715     webrtc->priv->pending_sink_transceivers = NULL;
3716 
3717     /* If connection's signaling state is now stable, update the
3718      * negotiation-needed flag. If connection's [[ needNegotiation]] slot
3719      * was true both before and after this update, queue a task to check
3720      * connection's [[needNegotiation]] slot and, if still true, fire a
3721      * simple event named negotiationneeded at connection.*/
3722     _update_need_negotiation (webrtc);
3723     if (prev_need_negotiation && webrtc->priv->need_negotiation) {
3724       _check_need_negotiation_task (webrtc, NULL);
3725     }
3726   }
3727 
3728   for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
3729     gchar *ufrag, *pwd;
3730     TransportStream *item;
3731 
3732     item =
3733         _get_or_create_transport_stream (webrtc, bundled ? bundle_idx : i,
3734         _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i));
3735 
3736     if (sd->source == SDP_REMOTE) {
3737       const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i);
3738       guint j;
3739 
3740       for (j = 0; j < gst_sdp_media_attributes_len (media); j++) {
3741         const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, j);
3742 
3743         if (g_strcmp0 (attr->key, "ssrc") == 0) {
3744           GStrv split = g_strsplit (attr->value, " ", 0);
3745           guint32 ssrc;
3746 
3747           if (split[0] && sscanf (split[0], "%u", &ssrc) && split[1]
3748               && g_str_has_prefix (split[1], "cname:")) {
3749             SsrcMapItem ssrc_item;
3750 
3751             ssrc_item.media_idx = i;
3752             ssrc_item.ssrc = ssrc;
3753             g_array_append_val (item->remote_ssrcmap, ssrc_item);
3754           }
3755           g_strfreev (split);
3756         }
3757       }
3758     }
3759 
3760     if (bundled && bundle_idx != i)
3761       continue;
3762 
3763     _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
3764 
3765     if (sd->source == SDP_LOCAL)
3766       gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
3767           item->stream, ufrag, pwd);
3768     else
3769       gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
3770           item->stream, ufrag, pwd);
3771     g_free (ufrag);
3772     g_free (pwd);
3773   }
3774 
3775   for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
3776     IceStreamItem *item =
3777         &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
3778 
3779     gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
3780   }
3781 
3782   if (webrtc->current_local_description && webrtc->current_remote_description) {
3783     int i;
3784 
3785     for (i = 0; i < webrtc->priv->pending_ice_candidates->len; i++) {
3786       IceCandidateItem *item =
3787           g_array_index (webrtc->priv->pending_ice_candidates,
3788           IceCandidateItem *, i);
3789 
3790       _add_ice_candidate (webrtc, item);
3791     }
3792     g_array_set_size (webrtc->priv->pending_ice_candidates, 0);
3793   }
3794 
3795 out:
3796   if (bundled)
3797     g_strfreev (bundled);
3798 
3799   PC_UNLOCK (webrtc);
3800   gst_promise_reply (sd->promise, NULL);
3801   PC_LOCK (webrtc);
3802 }
3803 
3804 static void
_free_set_description_data(struct set_description * sd)3805 _free_set_description_data (struct set_description *sd)
3806 {
3807   if (sd->promise)
3808     gst_promise_unref (sd->promise);
3809   if (sd->sdp)
3810     gst_webrtc_session_description_free (sd->sdp);
3811   g_free (sd);
3812 }
3813 
3814 static void
gst_webrtc_bin_set_remote_description(GstWebRTCBin * webrtc,GstWebRTCSessionDescription * remote_sdp,GstPromise * promise)3815 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
3816     GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
3817 {
3818   struct set_description *sd;
3819 
3820   if (remote_sdp == NULL)
3821     goto bad_input;
3822   if (remote_sdp->sdp == NULL)
3823     goto bad_input;
3824 
3825   sd = g_new0 (struct set_description, 1);
3826   if (promise != NULL)
3827     sd->promise = gst_promise_ref (promise);
3828   sd->source = SDP_REMOTE;
3829   sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
3830 
3831   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
3832       sd, (GDestroyNotify) _free_set_description_data);
3833 
3834   return;
3835 
3836 bad_input:
3837   {
3838     gst_promise_reply (promise, NULL);
3839     g_return_if_reached ();
3840   }
3841 }
3842 
3843 static void
gst_webrtc_bin_set_local_description(GstWebRTCBin * webrtc,GstWebRTCSessionDescription * local_sdp,GstPromise * promise)3844 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
3845     GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
3846 {
3847   struct set_description *sd;
3848 
3849   if (local_sdp == NULL)
3850     goto bad_input;
3851   if (local_sdp->sdp == NULL)
3852     goto bad_input;
3853 
3854   sd = g_new0 (struct set_description, 1);
3855   if (promise != NULL)
3856     sd->promise = gst_promise_ref (promise);
3857   sd->source = SDP_LOCAL;
3858   sd->sdp = gst_webrtc_session_description_copy (local_sdp);
3859 
3860   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
3861       sd, (GDestroyNotify) _free_set_description_data);
3862 
3863   return;
3864 
3865 bad_input:
3866   {
3867     gst_promise_reply (promise, NULL);
3868     g_return_if_reached ();
3869   }
3870 }
3871 
3872 static void
_add_ice_candidate_task(GstWebRTCBin * webrtc,IceCandidateItem * item)3873 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
3874 {
3875   if (!webrtc->current_local_description || !webrtc->current_remote_description) {
3876     IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
3877     new->mlineindex = item->mlineindex;
3878     new->candidate = g_strdup (item->candidate);
3879 
3880     g_array_append_val (webrtc->priv->pending_ice_candidates, new);
3881   } else {
3882     _add_ice_candidate (webrtc, item);
3883   }
3884 }
3885 
3886 static void
_free_ice_candidate_item(IceCandidateItem * item)3887 _free_ice_candidate_item (IceCandidateItem * item)
3888 {
3889   _clear_ice_candidate_item (&item);
3890 }
3891 
3892 static void
gst_webrtc_bin_add_ice_candidate(GstWebRTCBin * webrtc,guint mline,const gchar * attr)3893 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
3894     const gchar * attr)
3895 {
3896   IceCandidateItem *item;
3897 
3898   item = g_new0 (IceCandidateItem, 1);
3899   item->mlineindex = mline;
3900   if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
3901     item->candidate = g_strdup (attr);
3902   else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
3903     item->candidate = g_strdup_printf ("a=%s", attr);
3904   gst_webrtc_bin_enqueue_task (webrtc,
3905       (GstWebRTCBinFunc) _add_ice_candidate_task, item,
3906       (GDestroyNotify) _free_ice_candidate_item);
3907 }
3908 
3909 static void
_on_ice_candidate_task(GstWebRTCBin * webrtc,IceCandidateItem * item)3910 _on_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
3911 {
3912   const gchar *cand = item->candidate;
3913 
3914   if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
3915     /* stripping away "a=" */
3916     cand += 2;
3917   }
3918 
3919   GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
3920       item->mlineindex, cand);
3921 
3922   PC_UNLOCK (webrtc);
3923   g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
3924       0, item->mlineindex, cand);
3925   PC_LOCK (webrtc);
3926 }
3927 
3928 static void
_on_ice_candidate(GstWebRTCICE * ice,guint session_id,gchar * candidate,GstWebRTCBin * webrtc)3929 _on_ice_candidate (GstWebRTCICE * ice, guint session_id,
3930     gchar * candidate, GstWebRTCBin * webrtc)
3931 {
3932   IceCandidateItem *item = g_new0 (IceCandidateItem, 1);
3933 
3934   item->mlineindex = session_id;
3935   item->candidate = g_strdup (candidate);
3936 
3937   gst_webrtc_bin_enqueue_task (webrtc,
3938       (GstWebRTCBinFunc) _on_ice_candidate_task, item,
3939       (GDestroyNotify) _free_ice_candidate_item);
3940 }
3941 
3942 /* https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm */
3943 static GstStructure *
_get_stats_from_selector(GstWebRTCBin * webrtc,gpointer selector)3944 _get_stats_from_selector (GstWebRTCBin * webrtc, gpointer selector)
3945 {
3946   if (selector)
3947     GST_FIXME_OBJECT (webrtc, "Implement stats selection");
3948 
3949   return gst_structure_copy (webrtc->priv->stats);
3950 }
3951 
3952 struct get_stats
3953 {
3954   GstPad *pad;
3955   GstPromise *promise;
3956 };
3957 
3958 static void
_free_get_stats(struct get_stats * stats)3959 _free_get_stats (struct get_stats *stats)
3960 {
3961   if (stats->pad)
3962     gst_object_unref (stats->pad);
3963   if (stats->promise)
3964     gst_promise_unref (stats->promise);
3965   g_free (stats);
3966 }
3967 
3968 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
3969 static void
_get_stats_task(GstWebRTCBin * webrtc,struct get_stats * stats)3970 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
3971 {
3972   GstStructure *s;
3973   gpointer selector = NULL;
3974 
3975   gst_webrtc_bin_update_stats (webrtc);
3976 
3977   if (stats->pad) {
3978     GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (stats->pad);
3979 
3980     if (wpad->trans) {
3981       if (GST_PAD_DIRECTION (wpad) == GST_PAD_SRC) {
3982         selector = wpad->trans->receiver;
3983       } else {
3984         selector = wpad->trans->sender;
3985       }
3986     }
3987   }
3988 
3989   s = _get_stats_from_selector (webrtc, selector);
3990   gst_promise_reply (stats->promise, s);
3991 }
3992 
3993 static void
gst_webrtc_bin_get_stats(GstWebRTCBin * webrtc,GstPad * pad,GstPromise * promise)3994 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
3995     GstPromise * promise)
3996 {
3997   struct get_stats *stats;
3998 
3999   g_return_if_fail (promise != NULL);
4000   g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
4001 
4002   stats = g_new0 (struct get_stats, 1);
4003   stats->promise = gst_promise_ref (promise);
4004   /* FIXME: check that pad exists in element */
4005   if (pad)
4006     stats->pad = gst_object_ref (pad);
4007 
4008   gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
4009       stats, (GDestroyNotify) _free_get_stats);
4010 }
4011 
4012 static GstWebRTCRTPTransceiver *
gst_webrtc_bin_add_transceiver(GstWebRTCBin * webrtc,GstWebRTCRTPTransceiverDirection direction,GstCaps * caps)4013 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
4014     GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
4015 {
4016   WebRTCTransceiver *trans;
4017   GstWebRTCRTPTransceiver *rtp_trans;
4018 
4019   g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
4020       NULL);
4021 
4022   trans = _create_webrtc_transceiver (webrtc, direction, -1);
4023   rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
4024   if (caps)
4025     rtp_trans->codec_preferences = gst_caps_ref (caps);
4026 
4027   return gst_object_ref (trans);
4028 }
4029 
4030 static void
_deref_and_unref(GstObject ** object)4031 _deref_and_unref (GstObject ** object)
4032 {
4033   if (object)
4034     gst_object_unref (*object);
4035 }
4036 
4037 static GArray *
gst_webrtc_bin_get_transceivers(GstWebRTCBin * webrtc)4038 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
4039 {
4040   GArray *arr = g_array_new (FALSE, TRUE, sizeof (gpointer));
4041   int i;
4042 
4043   g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
4044 
4045   for (i = 0; i < webrtc->priv->transceivers->len; i++) {
4046     GstWebRTCRTPTransceiver *trans =
4047         g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
4048         i);
4049     gst_object_ref (trans);
4050     g_array_append_val (arr, trans);
4051   }
4052 
4053   return arr;
4054 }
4055 
4056 static GstWebRTCRTPTransceiver *
gst_webrtc_bin_get_transceiver(GstWebRTCBin * webrtc,guint idx)4057 gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx)
4058 {
4059   GstWebRTCRTPTransceiver *trans = NULL;
4060 
4061   if (idx >= webrtc->priv->transceivers->len) {
4062     GST_ERROR_OBJECT (webrtc, "No transceiver for idx %d", idx);
4063     goto done;
4064   }
4065 
4066   trans =
4067       g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
4068       idx);
4069   gst_object_ref (trans);
4070 
4071 done:
4072   return trans;
4073 }
4074 
4075 static gboolean
gst_webrtc_bin_add_turn_server(GstWebRTCBin * webrtc,const gchar * uri)4076 gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
4077 {
4078   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
4079   g_return_val_if_fail (uri != NULL, FALSE);
4080 
4081   GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
4082 
4083   return gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
4084 }
4085 
4086 static gboolean
copy_sticky_events(GstPad * pad,GstEvent ** event,gpointer user_data)4087 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
4088 {
4089   GstPad *gpad = GST_PAD_CAST (user_data);
4090 
4091   GST_DEBUG_OBJECT (gpad, "store sticky event %" GST_PTR_FORMAT, *event);
4092   gst_pad_store_sticky_event (gpad, *event);
4093 
4094   return TRUE;
4095 }
4096 
4097 static GstWebRTCDataChannel *
gst_webrtc_bin_create_data_channel(GstWebRTCBin * webrtc,const gchar * label,GstStructure * init_params)4098 gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
4099     GstStructure * init_params)
4100 {
4101   gboolean ordered;
4102   gint max_packet_lifetime;
4103   gint max_retransmits;
4104   const gchar *protocol;
4105   gboolean negotiated;
4106   gint id;
4107   GstWebRTCPriorityType priority;
4108   GstWebRTCDataChannel *ret;
4109   gint max_channels = 65534;
4110 
4111   g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL);
4112   g_return_val_if_fail (label != NULL, NULL);
4113   g_return_val_if_fail (strlen (label) <= 65535, NULL);
4114   g_return_val_if_fail (webrtc->priv->is_closed != TRUE, NULL);
4115 
4116   if (!init_params
4117       || !gst_structure_get_boolean (init_params, "ordered", &ordered))
4118     ordered = TRUE;
4119   if (!init_params
4120       || !gst_structure_get_int (init_params, "max-packet-lifetime",
4121           &max_packet_lifetime))
4122     max_packet_lifetime = -1;
4123   if (!init_params
4124       || !gst_structure_get_int (init_params, "max-retransmits",
4125           &max_retransmits))
4126     max_retransmits = -1;
4127   /* both retransmits and lifetime cannot be set */
4128   g_return_val_if_fail ((max_packet_lifetime == -1)
4129       || (max_retransmits == -1), NULL);
4130 
4131   if (!init_params
4132       || !(protocol = gst_structure_get_string (init_params, "protocol")))
4133     protocol = "";
4134   g_return_val_if_fail (strlen (protocol) <= 65535, NULL);
4135 
4136   if (!init_params
4137       || !gst_structure_get_boolean (init_params, "negotiated", &negotiated))
4138     negotiated = FALSE;
4139   if (!negotiated || !init_params
4140       || !gst_structure_get_int (init_params, "id", &id))
4141     id = -1;
4142   if (negotiated)
4143     g_return_val_if_fail (id != -1, NULL);
4144   g_return_val_if_fail (id < 65535, NULL);
4145 
4146   if (!init_params
4147       || !gst_structure_get_enum (init_params, "priority",
4148           GST_TYPE_WEBRTC_PRIORITY_TYPE, (gint *) & priority))
4149     priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
4150 
4151   /* FIXME: clamp max-retransmits and max-packet-lifetime */
4152 
4153   if (webrtc->priv->sctp_transport) {
4154     /* Let transport be the connection's [[SctpTransport]] slot.
4155      *
4156      * If the [[DataChannelId]] slot is not null, transport is in
4157      * connected state and [[DataChannelId]] is greater or equal to the
4158      * transport's [[MaxChannels]] slot, throw an OperationError.
4159      */
4160     g_object_get (webrtc->priv->sctp_transport, "max-channels", &max_channels,
4161         NULL);
4162 
4163     g_return_val_if_fail (id <= max_channels, NULL);
4164   }
4165 
4166   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc) ||
4167       !_have_sctp_elements (webrtc))
4168     return NULL;
4169 
4170   PC_LOCK (webrtc);
4171   /* check if the id has been used already */
4172   if (id != -1) {
4173     GstWebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id);
4174     if (channel) {
4175       GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS,
4176           ("Attempting to add a data channel with a duplicate ID: %i", id),
4177           NULL);
4178       PC_UNLOCK (webrtc);
4179       return NULL;
4180     }
4181   } else if (webrtc->current_local_description
4182       && webrtc->current_remote_description && webrtc->priv->sctp_transport
4183       && webrtc->priv->sctp_transport->transport) {
4184     /* else we can only generate an id if we're configured already.  The other
4185      * case for generating an id is on sdp setting */
4186     id = _generate_data_channel_id (webrtc);
4187     if (id == -1) {
4188       GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND,
4189           ("%s", "Failed to generate an identifier for a data channel"), NULL);
4190       PC_UNLOCK (webrtc);
4191       return NULL;
4192     }
4193   }
4194 
4195   ret = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, "label", label,
4196       "ordered", ordered, "max-packet-lifetime", max_packet_lifetime,
4197       "max-retransmits", max_retransmits, "protocol", protocol,
4198       "negotiated", negotiated, "id", id, "priority", priority, NULL);
4199 
4200   if (ret) {
4201     gst_bin_add (GST_BIN (webrtc), ret->appsrc);
4202     gst_bin_add (GST_BIN (webrtc), ret->appsink);
4203 
4204     gst_element_sync_state_with_parent (ret->appsrc);
4205     gst_element_sync_state_with_parent (ret->appsink);
4206 
4207     ret = gst_object_ref (ret);
4208     ret->webrtcbin = webrtc;
4209     g_array_append_val (webrtc->priv->data_channels, ret);
4210     _link_data_channel_to_sctp (webrtc, ret);
4211     if (webrtc->priv->sctp_transport &&
4212         webrtc->priv->sctp_transport->association_established
4213         && !ret->negotiated)
4214       gst_webrtc_data_channel_start_negotiation (ret);
4215   }
4216 
4217   PC_UNLOCK (webrtc);
4218   return ret;
4219 }
4220 
4221 /* === rtpbin signal implementations === */
4222 
4223 static void
on_rtpbin_pad_added(GstElement * rtpbin,GstPad * new_pad,GstWebRTCBin * webrtc)4224 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
4225     GstWebRTCBin * webrtc)
4226 {
4227   gchar *new_pad_name = NULL;
4228 
4229   new_pad_name = gst_pad_get_name (new_pad);
4230   GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
4231   if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
4232     guint32 session_id = 0, ssrc = 0, pt = 0;
4233     GstWebRTCRTPTransceiver *rtp_trans;
4234     WebRTCTransceiver *trans;
4235     TransportStream *stream;
4236     GstWebRTCBinPad *pad;
4237     guint media_idx = 0;
4238     guint i;
4239 
4240     if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc,
4241             &pt) != 3) {
4242       g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
4243       return;
4244     }
4245 
4246     stream = _find_transport_for_session (webrtc, session_id);
4247     if (!stream)
4248       g_warn_if_reached ();
4249 
4250     media_idx = session_id;
4251 
4252     for (i = 0; i < stream->remote_ssrcmap->len; i++) {
4253       SsrcMapItem *item =
4254           &g_array_index (stream->remote_ssrcmap, SsrcMapItem, i);
4255       if (item->ssrc == ssrc) {
4256         media_idx = item->media_idx;
4257         break;
4258       }
4259     }
4260 
4261     rtp_trans = _find_transceiver_for_mline (webrtc, media_idx);
4262     if (!rtp_trans)
4263       g_warn_if_reached ();
4264     trans = WEBRTC_TRANSCEIVER (rtp_trans);
4265     g_assert (trans->stream == stream);
4266 
4267     pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
4268 
4269     GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
4270         " for rtpbin pad name %s", pad, new_pad_name);
4271     if (!pad)
4272       g_warn_if_reached ();
4273     gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
4274 
4275     if (webrtc->priv->running)
4276       gst_pad_set_active (GST_PAD (pad), TRUE);
4277     gst_pad_sticky_events_foreach (new_pad, copy_sticky_events, pad);
4278     gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
4279     _remove_pending_pad (webrtc, pad);
4280 
4281     gst_object_unref (pad);
4282   }
4283   g_free (new_pad_name);
4284 }
4285 
4286 /* only used for the receiving streams */
4287 static GstCaps *
on_rtpbin_request_pt_map(GstElement * rtpbin,guint session_id,guint pt,GstWebRTCBin * webrtc)4288 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
4289     GstWebRTCBin * webrtc)
4290 {
4291   TransportStream *stream;
4292   GstCaps *ret;
4293 
4294   GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
4295       session_id);
4296 
4297   stream = _find_transport_for_session (webrtc, session_id);
4298   if (!stream)
4299     goto unknown_session;
4300 
4301   if ((ret = transport_stream_get_caps_for_pt (stream, pt)))
4302     gst_caps_ref (ret);
4303 
4304   GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
4305       "session %d", ret, pt, session_id);
4306 
4307   return ret;
4308 
4309 unknown_session:
4310   {
4311     GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
4312     return NULL;
4313   }
4314 }
4315 
4316 static GstElement *
on_rtpbin_request_aux_sender(GstElement * rtpbin,guint session_id,GstWebRTCBin * webrtc)4317 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
4318     GstWebRTCBin * webrtc)
4319 {
4320   TransportStream *stream;
4321   GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4322   GstElement *ret = NULL;
4323   GstWebRTCRTPTransceiver *trans;
4324 
4325   stream = _find_transport_for_session (webrtc, session_id);
4326   trans = _find_transceiver (webrtc, &session_id,
4327       (FindTransceiverFunc) transceiver_match_for_mline);
4328 
4329   if (stream) {
4330     guint i;
4331 
4332     for (i = 0; i < stream->ptmap->len; i++) {
4333       PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
4334       if (!gst_caps_is_empty (item->caps)) {
4335         GstStructure *s = gst_caps_get_structure (item->caps, 0);
4336         gint pt;
4337         const gchar *apt_str = gst_structure_get_string (s, "apt");
4338 
4339         if (!apt_str)
4340           continue;
4341 
4342         if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "RTX") &&
4343             gst_structure_get_int (s, "payload", &pt)) {
4344           gst_structure_set (pt_map, apt_str, G_TYPE_UINT, pt, NULL);
4345         }
4346       }
4347     }
4348   }
4349 
4350   GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT
4351       " with transport %" GST_PTR_FORMAT " and pt map %" GST_PTR_FORMAT, stream,
4352       trans, pt_map);
4353 
4354   if (gst_structure_n_fields (pt_map)) {
4355     GstElement *rtx;
4356     GstPad *pad;
4357     gchar *name;
4358 
4359     GST_INFO ("creating AUX sender");
4360     ret = gst_bin_new (NULL);
4361     rtx = gst_element_factory_make ("rtprtxsend", NULL);
4362     g_object_set (rtx, "payload-type-map", pt_map, "max-size-packets", 500,
4363         NULL);
4364 
4365     if (WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map)
4366       g_object_set (rtx, "ssrc-map",
4367           WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map, NULL);
4368 
4369     gst_bin_add (GST_BIN (ret), rtx);
4370 
4371     pad = gst_element_get_static_pad (rtx, "src");
4372     name = g_strdup_printf ("src_%u", session_id);
4373     gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
4374     g_free (name);
4375     gst_object_unref (pad);
4376 
4377     pad = gst_element_get_static_pad (rtx, "sink");
4378     name = g_strdup_printf ("sink_%u", session_id);
4379     gst_element_add_pad (ret, gst_ghost_pad_new (name, pad));
4380     g_free (name);
4381     gst_object_unref (pad);
4382   }
4383 
4384   gst_structure_free (pt_map);
4385 
4386   return ret;
4387 }
4388 
4389 static GstElement *
on_rtpbin_request_aux_receiver(GstElement * rtpbin,guint session_id,GstWebRTCBin * webrtc)4390 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
4391     GstWebRTCBin * webrtc)
4392 {
4393   GstElement *ret = NULL;
4394   GstElement *prev = NULL;
4395   GstPad *sinkpad = NULL;
4396   TransportStream *stream;
4397   gint red_pt = 0;
4398   gint rtx_pt = 0;
4399 
4400   stream = _find_transport_for_session (webrtc, session_id);
4401 
4402   if (stream) {
4403     red_pt = transport_stream_get_pt (stream, "RED");
4404     rtx_pt = transport_stream_get_pt (stream, "RTX");
4405   }
4406 
4407   GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT
4408       " with pt red:%u rtx:%u", stream, red_pt, rtx_pt);
4409 
4410   if (red_pt || rtx_pt)
4411     ret = gst_bin_new (NULL);
4412 
4413   if (rtx_pt) {
4414     GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt);
4415     GstElement *rtx = gst_element_factory_make ("rtprtxreceive", NULL);
4416     GstStructure *pt_map;
4417     const GstStructure *s = gst_caps_get_structure (rtx_caps, 0);
4418 
4419     gst_bin_add (GST_BIN (ret), rtx);
4420 
4421     pt_map = gst_structure_new_empty ("application/x-rtp-pt-map");
4422     gst_structure_set (pt_map, gst_structure_get_string (s, "apt"), G_TYPE_UINT,
4423         rtx_pt, NULL);
4424     g_object_set (rtx, "payload-type-map", pt_map, NULL);
4425 
4426     sinkpad = gst_element_get_static_pad (rtx, "sink");
4427 
4428     prev = rtx;
4429   }
4430 
4431   if (red_pt) {
4432     GstElement *rtpreddec = gst_element_factory_make ("rtpreddec", NULL);
4433 
4434     GST_DEBUG_OBJECT (webrtc, "Creating RED decoder for pt %d in session %u",
4435         red_pt, session_id);
4436 
4437     gst_bin_add (GST_BIN (ret), rtpreddec);
4438 
4439     g_object_set (rtpreddec, "pt", red_pt, NULL);
4440 
4441     if (prev)
4442       gst_element_link (prev, rtpreddec);
4443     else
4444       sinkpad = gst_element_get_static_pad (rtpreddec, "sink");
4445 
4446     prev = rtpreddec;
4447   }
4448 
4449   if (sinkpad) {
4450     gchar *name = g_strdup_printf ("sink_%u", session_id);
4451     GstPad *ghost = gst_ghost_pad_new (name, sinkpad);
4452     g_free (name);
4453     gst_object_unref (sinkpad);
4454     gst_element_add_pad (ret, ghost);
4455   }
4456 
4457   if (prev) {
4458     gchar *name = g_strdup_printf ("src_%u", session_id);
4459     GstPad *srcpad = gst_element_get_static_pad (prev, "src");
4460     GstPad *ghost = gst_ghost_pad_new (name, srcpad);
4461     g_free (name);
4462     gst_object_unref (srcpad);
4463     gst_element_add_pad (ret, ghost);
4464   }
4465 
4466   return ret;
4467 }
4468 
4469 static GstElement *
on_rtpbin_request_fec_decoder(GstElement * rtpbin,guint session_id,GstWebRTCBin * webrtc)4470 on_rtpbin_request_fec_decoder (GstElement * rtpbin, guint session_id,
4471     GstWebRTCBin * webrtc)
4472 {
4473   TransportStream *stream;
4474   GstElement *ret = NULL;
4475   gint pt = 0;
4476   GObject *internal_storage;
4477 
4478   stream = _find_transport_for_session (webrtc, session_id);
4479 
4480   /* TODO: for now, we only support ulpfec, but once we support
4481    * more algorithms, if the remote may use more than one algorithm,
4482    * we will want to do the following:
4483    *
4484    * + Return a bin here, with the relevant FEC decoders plugged in
4485    *   and their payload type set to 0
4486    * + Enable the decoders by setting the payload type only when
4487    *   we detect it (by connecting to ptdemux:new-payload-type for
4488    *   example)
4489    */
4490   if (stream)
4491     pt = transport_stream_get_pt (stream, "ULPFEC");
4492 
4493   if (pt) {
4494     GST_DEBUG_OBJECT (webrtc, "Creating ULPFEC decoder for pt %d in session %u",
4495         pt, session_id);
4496     ret = gst_element_factory_make ("rtpulpfecdec", NULL);
4497     g_signal_emit_by_name (webrtc->rtpbin, "get-internal-storage", session_id,
4498         &internal_storage);
4499 
4500     g_object_set (ret, "pt", pt, "storage", internal_storage, NULL);
4501     g_object_unref (internal_storage);
4502   }
4503 
4504   return ret;
4505 }
4506 
4507 static GstElement *
on_rtpbin_request_fec_encoder(GstElement * rtpbin,guint session_id,GstWebRTCBin * webrtc)4508 on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id,
4509     GstWebRTCBin * webrtc)
4510 {
4511   GstElement *ret = NULL;
4512   GstElement *prev = NULL;
4513   TransportStream *stream;
4514   guint ulpfec_pt = 0;
4515   guint red_pt = 0;
4516   GstPad *sinkpad = NULL;
4517   GstWebRTCRTPTransceiver *trans;
4518 
4519   stream = _find_transport_for_session (webrtc, session_id);
4520   trans = _find_transceiver (webrtc, &session_id,
4521       (FindTransceiverFunc) transceiver_match_for_mline);
4522 
4523   if (stream) {
4524     ulpfec_pt = transport_stream_get_pt (stream, "ULPFEC");
4525     red_pt = transport_stream_get_pt (stream, "RED");
4526   }
4527 
4528   if (ulpfec_pt || red_pt)
4529     ret = gst_bin_new (NULL);
4530 
4531   if (ulpfec_pt) {
4532     GstElement *fecenc = gst_element_factory_make ("rtpulpfecenc", NULL);
4533     GstCaps *caps = transport_stream_get_caps_for_pt (stream, ulpfec_pt);
4534 
4535     GST_DEBUG_OBJECT (webrtc,
4536         "Creating ULPFEC encoder for session %d with pt %d", session_id,
4537         ulpfec_pt);
4538 
4539     gst_bin_add (GST_BIN (ret), fecenc);
4540     sinkpad = gst_element_get_static_pad (fecenc, "sink");
4541     g_object_set (fecenc, "pt", ulpfec_pt, "percentage",
4542         WEBRTC_TRANSCEIVER (trans)->fec_percentage, NULL);
4543 
4544 
4545     if (caps && !gst_caps_is_empty (caps)) {
4546       const GstStructure *s = gst_caps_get_structure (caps, 0);
4547       const gchar *media = gst_structure_get_string (s, "media");
4548 
4549       if (!g_strcmp0 (media, "video"))
4550         g_object_set (fecenc, "multipacket", TRUE, NULL);
4551     }
4552 
4553     prev = fecenc;
4554   }
4555 
4556   if (red_pt) {
4557     GstElement *redenc = gst_element_factory_make ("rtpredenc", NULL);
4558 
4559     GST_DEBUG_OBJECT (webrtc, "Creating RED encoder for session %d with pt %d",
4560         session_id, red_pt);
4561 
4562     gst_bin_add (GST_BIN (ret), redenc);
4563     if (prev)
4564       gst_element_link (prev, redenc);
4565     else
4566       sinkpad = gst_element_get_static_pad (redenc, "sink");
4567 
4568     g_object_set (redenc, "pt", red_pt, "allow-no-red-blocks", TRUE, NULL);
4569 
4570     prev = redenc;
4571   }
4572 
4573   if (sinkpad) {
4574     GstPad *ghost = gst_ghost_pad_new ("sink", sinkpad);
4575     gst_object_unref (sinkpad);
4576     gst_element_add_pad (ret, ghost);
4577   }
4578 
4579   if (prev) {
4580     GstPad *srcpad = gst_element_get_static_pad (prev, "src");
4581     GstPad *ghost = gst_ghost_pad_new ("src", srcpad);
4582     gst_object_unref (srcpad);
4583     gst_element_add_pad (ret, ghost);
4584   }
4585 
4586   return ret;
4587 }
4588 
4589 static void
on_rtpbin_ssrc_active(GstElement * rtpbin,guint session_id,guint ssrc,GstWebRTCBin * webrtc)4590 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
4591     GstWebRTCBin * webrtc)
4592 {
4593 }
4594 
4595 static void
on_rtpbin_new_jitterbuffer(GstElement * rtpbin,GstElement * jitterbuffer,guint session_id,guint ssrc,GstWebRTCBin * webrtc)4596 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
4597     guint session_id, guint ssrc, GstWebRTCBin * webrtc)
4598 {
4599   GstWebRTCRTPTransceiver *trans;
4600 
4601   trans = _find_transceiver (webrtc, &session_id,
4602       (FindTransceiverFunc) transceiver_match_for_mline);
4603 
4604   if (trans) {
4605     /* We don't set do-retransmission on rtpbin as we want per-session control */
4606     g_object_set (jitterbuffer, "do-retransmission",
4607         WEBRTC_TRANSCEIVER (trans)->do_nack, NULL);
4608   } else {
4609     g_assert_not_reached ();
4610   }
4611 }
4612 
4613 static void
on_rtpbin_new_storage(GstElement * rtpbin,GstElement * storage,guint session_id,GstWebRTCBin * webrtc)4614 on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage,
4615     guint session_id, GstWebRTCBin * webrtc)
4616 {
4617   /* TODO: when exposing latency, set size-time based on that */
4618   g_object_set (storage, "size-time", (guint64) 250 * GST_MSECOND, NULL);
4619 }
4620 
4621 static GstElement *
_create_rtpbin(GstWebRTCBin * webrtc)4622 _create_rtpbin (GstWebRTCBin * webrtc)
4623 {
4624   GstElement *rtpbin;
4625 
4626   if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
4627     return NULL;
4628 
4629   /* mandated by WebRTC */
4630   gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
4631 
4632   g_object_set (rtpbin, "do-lost", TRUE, NULL);
4633 
4634   g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
4635       webrtc);
4636   g_signal_connect (rtpbin, "request-pt-map",
4637       G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
4638   g_signal_connect (rtpbin, "request-aux-sender",
4639       G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
4640   g_signal_connect (rtpbin, "request-aux-receiver",
4641       G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
4642   g_signal_connect (rtpbin, "new-storage",
4643       G_CALLBACK (on_rtpbin_new_storage), webrtc);
4644   g_signal_connect (rtpbin, "request-fec-decoder",
4645       G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc);
4646   g_signal_connect (rtpbin, "request-fec-encoder",
4647       G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc);
4648   g_signal_connect (rtpbin, "on-ssrc-active",
4649       G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
4650   g_signal_connect (rtpbin, "new-jitterbuffer",
4651       G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
4652 
4653   return rtpbin;
4654 }
4655 
4656 static GstStateChangeReturn
gst_webrtc_bin_change_state(GstElement * element,GstStateChange transition)4657 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
4658 {
4659   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
4660   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
4661 
4662   GST_DEBUG ("changing state: %s => %s",
4663       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
4664       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
4665 
4666   switch (transition) {
4667     case GST_STATE_CHANGE_NULL_TO_READY:{
4668       if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
4669         return GST_STATE_CHANGE_FAILURE;
4670       _start_thread (webrtc);
4671       _update_need_negotiation (webrtc);
4672       break;
4673     }
4674     case GST_STATE_CHANGE_READY_TO_PAUSED:
4675       webrtc->priv->running = TRUE;
4676       break;
4677     default:
4678       break;
4679   }
4680 
4681   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
4682   if (ret == GST_STATE_CHANGE_FAILURE)
4683     return ret;
4684 
4685   switch (transition) {
4686     case GST_STATE_CHANGE_READY_TO_PAUSED:
4687       /* Mangle the return value to NO_PREROLL as that's what really is
4688        * occurring here however cannot be propagated correctly due to nicesrc
4689        * requiring that it be in PLAYING already in order to send/receive
4690        * correctly :/ */
4691       ret = GST_STATE_CHANGE_NO_PREROLL;
4692       break;
4693     case GST_STATE_CHANGE_PAUSED_TO_READY:
4694       webrtc->priv->running = FALSE;
4695       break;
4696     case GST_STATE_CHANGE_READY_TO_NULL:
4697       _stop_thread (webrtc);
4698       break;
4699     default:
4700       break;
4701   }
4702 
4703   return ret;
4704 }
4705 
4706 static GstPadProbeReturn
pad_block(GstPad * pad,GstPadProbeInfo * info,gpointer unused)4707 pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused)
4708 {
4709   GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data);
4710 
4711   return GST_PAD_PROBE_OK;
4712 }
4713 
4714 static GstPad *
gst_webrtc_bin_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)4715 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
4716     const gchar * name, const GstCaps * caps)
4717 {
4718   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
4719   GstWebRTCBinPad *pad = NULL;
4720   guint serial;
4721 
4722   if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc))
4723     return NULL;
4724 
4725   if (templ->direction == GST_PAD_SINK ||
4726       g_strcmp0 (templ->name_template, "sink_%u") == 0) {
4727     GstWebRTCRTPTransceiver *trans;
4728 
4729     GST_OBJECT_LOCK (webrtc);
4730     if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
4731       /* no name given when requesting the pad, use next available int */
4732       serial = webrtc->priv->max_sink_pad_serial++;
4733     } else {
4734       /* parse serial number from requested padname */
4735       serial = g_ascii_strtoull (&name[5], NULL, 10);
4736       if (serial > webrtc->priv->max_sink_pad_serial)
4737         webrtc->priv->max_sink_pad_serial = serial;
4738     }
4739     GST_OBJECT_UNLOCK (webrtc);
4740 
4741     pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial);
4742     trans = _find_transceiver_for_mline (webrtc, serial);
4743     if (!trans)
4744       trans =
4745           GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc,
4746               GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, serial));
4747     pad->trans = gst_object_ref (trans);
4748 
4749     pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
4750         GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
4751         (GstPadProbeCallback) pad_block, NULL, NULL);
4752     webrtc->priv->pending_sink_transceivers =
4753         g_list_append (webrtc->priv->pending_sink_transceivers,
4754         gst_object_ref (pad));
4755     _add_pad (webrtc, pad);
4756   }
4757 
4758   return GST_PAD (pad);
4759 }
4760 
4761 static void
gst_webrtc_bin_release_pad(GstElement * element,GstPad * pad)4762 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
4763 {
4764   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
4765   GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
4766 
4767   if (webrtc_pad->trans)
4768     gst_object_unref (webrtc_pad->trans);
4769   webrtc_pad->trans = NULL;
4770 
4771   _remove_pad (webrtc, webrtc_pad);
4772 }
4773 
4774 static void
gst_webrtc_bin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)4775 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
4776     const GValue * value, GParamSpec * pspec)
4777 {
4778   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
4779 
4780   switch (prop_id) {
4781     case PROP_STUN_SERVER:
4782     case PROP_TURN_SERVER:
4783       g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
4784       break;
4785     case PROP_BUNDLE_POLICY:
4786       if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) {
4787         GST_ERROR_OBJECT (object, "Balanced bundle policy not implemented yet");
4788       } else {
4789         webrtc->bundle_policy = g_value_get_enum (value);
4790       }
4791       break;
4792     case PROP_ICE_TRANSPORT_POLICY:
4793       webrtc->ice_transport_policy = g_value_get_enum (value);
4794       g_object_set (webrtc->priv->ice, "force-relay",
4795           webrtc->ice_transport_policy ==
4796           GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE, NULL);
4797       break;
4798     default:
4799       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4800       break;
4801   }
4802 }
4803 
4804 static void
gst_webrtc_bin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)4805 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
4806     GValue * value, GParamSpec * pspec)
4807 {
4808   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
4809 
4810   PC_LOCK (webrtc);
4811   switch (prop_id) {
4812     case PROP_CONNECTION_STATE:
4813       g_value_set_enum (value, webrtc->peer_connection_state);
4814       break;
4815     case PROP_SIGNALING_STATE:
4816       g_value_set_enum (value, webrtc->signaling_state);
4817       break;
4818     case PROP_ICE_GATHERING_STATE:
4819       g_value_set_enum (value, webrtc->ice_gathering_state);
4820       break;
4821     case PROP_ICE_CONNECTION_STATE:
4822       g_value_set_enum (value, webrtc->ice_connection_state);
4823       break;
4824     case PROP_LOCAL_DESCRIPTION:
4825       if (webrtc->pending_local_description)
4826         g_value_set_boxed (value, webrtc->pending_local_description);
4827       else if (webrtc->current_local_description)
4828         g_value_set_boxed (value, webrtc->current_local_description);
4829       else
4830         g_value_set_boxed (value, NULL);
4831       break;
4832     case PROP_CURRENT_LOCAL_DESCRIPTION:
4833       g_value_set_boxed (value, webrtc->current_local_description);
4834       break;
4835     case PROP_PENDING_LOCAL_DESCRIPTION:
4836       g_value_set_boxed (value, webrtc->pending_local_description);
4837       break;
4838     case PROP_REMOTE_DESCRIPTION:
4839       if (webrtc->pending_remote_description)
4840         g_value_set_boxed (value, webrtc->pending_remote_description);
4841       else if (webrtc->current_remote_description)
4842         g_value_set_boxed (value, webrtc->current_remote_description);
4843       else
4844         g_value_set_boxed (value, NULL);
4845       break;
4846     case PROP_CURRENT_REMOTE_DESCRIPTION:
4847       g_value_set_boxed (value, webrtc->current_remote_description);
4848       break;
4849     case PROP_PENDING_REMOTE_DESCRIPTION:
4850       g_value_set_boxed (value, webrtc->pending_remote_description);
4851       break;
4852     case PROP_STUN_SERVER:
4853     case PROP_TURN_SERVER:
4854       g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
4855       break;
4856     case PROP_BUNDLE_POLICY:
4857       g_value_set_enum (value, webrtc->bundle_policy);
4858       break;
4859     case PROP_ICE_TRANSPORT_POLICY:
4860       g_value_set_enum (value, webrtc->ice_transport_policy);
4861       break;
4862     default:
4863       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
4864       break;
4865   }
4866   PC_UNLOCK (webrtc);
4867 }
4868 
4869 static void
_free_pending_pad(GstPad * pad)4870 _free_pending_pad (GstPad * pad)
4871 {
4872   gst_object_unref (pad);
4873 }
4874 
4875 static void
gst_webrtc_bin_dispose(GObject * object)4876 gst_webrtc_bin_dispose (GObject * object)
4877 {
4878   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
4879 
4880   if (webrtc->priv->ice)
4881     gst_object_unref (webrtc->priv->ice);
4882   webrtc->priv->ice = NULL;
4883 
4884   if (webrtc->priv->ice_stream_map)
4885     g_array_free (webrtc->priv->ice_stream_map, TRUE);
4886   webrtc->priv->ice_stream_map = NULL;
4887 
4888   g_clear_object (&webrtc->priv->sctp_transport);
4889 
4890   G_OBJECT_CLASS (parent_class)->dispose (object);
4891 }
4892 
4893 static void
gst_webrtc_bin_finalize(GObject * object)4894 gst_webrtc_bin_finalize (GObject * object)
4895 {
4896   GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
4897 
4898   if (webrtc->priv->transports)
4899     g_array_free (webrtc->priv->transports, TRUE);
4900   webrtc->priv->transports = NULL;
4901 
4902   if (webrtc->priv->transceivers)
4903     g_array_free (webrtc->priv->transceivers, TRUE);
4904   webrtc->priv->transceivers = NULL;
4905 
4906   if (webrtc->priv->data_channels)
4907     g_array_free (webrtc->priv->data_channels, TRUE);
4908   webrtc->priv->data_channels = NULL;
4909 
4910   if (webrtc->priv->pending_data_channels)
4911     g_array_free (webrtc->priv->pending_data_channels, TRUE);
4912   webrtc->priv->pending_data_channels = NULL;
4913 
4914   if (webrtc->priv->pending_ice_candidates)
4915     g_array_free (webrtc->priv->pending_ice_candidates, TRUE);
4916   webrtc->priv->pending_ice_candidates = NULL;
4917 
4918   if (webrtc->priv->session_mid_map)
4919     g_array_free (webrtc->priv->session_mid_map, TRUE);
4920   webrtc->priv->session_mid_map = NULL;
4921 
4922   if (webrtc->priv->pending_pads)
4923     g_list_free_full (webrtc->priv->pending_pads,
4924         (GDestroyNotify) _free_pending_pad);
4925   webrtc->priv->pending_pads = NULL;
4926 
4927   if (webrtc->priv->pending_sink_transceivers)
4928     g_list_free_full (webrtc->priv->pending_sink_transceivers,
4929         (GDestroyNotify) gst_object_unref);
4930   webrtc->priv->pending_sink_transceivers = NULL;
4931 
4932   if (webrtc->current_local_description)
4933     gst_webrtc_session_description_free (webrtc->current_local_description);
4934   webrtc->current_local_description = NULL;
4935   if (webrtc->pending_local_description)
4936     gst_webrtc_session_description_free (webrtc->pending_local_description);
4937   webrtc->pending_local_description = NULL;
4938 
4939   if (webrtc->current_remote_description)
4940     gst_webrtc_session_description_free (webrtc->current_remote_description);
4941   webrtc->current_remote_description = NULL;
4942   if (webrtc->pending_remote_description)
4943     gst_webrtc_session_description_free (webrtc->pending_remote_description);
4944   webrtc->pending_remote_description = NULL;
4945 
4946   if (webrtc->priv->stats)
4947     gst_structure_free (webrtc->priv->stats);
4948   webrtc->priv->stats = NULL;
4949 
4950   g_mutex_clear (PC_GET_LOCK (webrtc));
4951   g_cond_clear (PC_GET_COND (webrtc));
4952 
4953   G_OBJECT_CLASS (parent_class)->finalize (object);
4954 }
4955 
4956 static void
gst_webrtc_bin_class_init(GstWebRTCBinClass * klass)4957 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
4958 {
4959   GObjectClass *gobject_class = (GObjectClass *) klass;
4960   GstElementClass *element_class = (GstElementClass *) klass;
4961 
4962   element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
4963   element_class->release_pad = gst_webrtc_bin_release_pad;
4964   element_class->change_state = gst_webrtc_bin_change_state;
4965 
4966   gst_element_class_add_static_pad_template_with_gtype (element_class,
4967       &sink_template, GST_TYPE_WEBRTC_BIN_PAD);
4968   gst_element_class_add_static_pad_template (element_class, &src_template);
4969 
4970   gst_element_class_set_metadata (element_class, "WebRTC Bin",
4971       "Filter/Network/WebRTC", "A bin for webrtc connections",
4972       "Matthew Waters <matthew@centricular.com>");
4973 
4974   gobject_class->get_property = gst_webrtc_bin_get_property;
4975   gobject_class->set_property = gst_webrtc_bin_set_property;
4976   gobject_class->dispose = gst_webrtc_bin_dispose;
4977   gobject_class->finalize = gst_webrtc_bin_finalize;
4978 
4979   g_object_class_install_property (gobject_class,
4980       PROP_LOCAL_DESCRIPTION,
4981       g_param_spec_boxed ("local-description", "Local Description",
4982           "The local SDP description to use for this connection",
4983           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
4984           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4985 
4986   g_object_class_install_property (gobject_class,
4987       PROP_REMOTE_DESCRIPTION,
4988       g_param_spec_boxed ("remote-description", "Remote Description",
4989           "The remote SDP description to use for this connection",
4990           GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
4991           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4992 
4993   g_object_class_install_property (gobject_class,
4994       PROP_STUN_SERVER,
4995       g_param_spec_string ("stun-server", "STUN Server",
4996           "The STUN server of the form stun://hostname:port",
4997           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
4998 
4999   g_object_class_install_property (gobject_class,
5000       PROP_TURN_SERVER,
5001       g_param_spec_string ("turn-server", "TURN Server",
5002           "The TURN server of the form turn(s)://username:password@host:port. "
5003           "This is a convenience property, use #GstWebRTCBin::add-turn-server "
5004           "if you wish to use multiple TURN servers",
5005           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5006 
5007   g_object_class_install_property (gobject_class,
5008       PROP_CONNECTION_STATE,
5009       g_param_spec_enum ("connection-state", "Connection State",
5010           "The overall connection state of this element",
5011           GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
5012           GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
5013           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5014 
5015   g_object_class_install_property (gobject_class,
5016       PROP_SIGNALING_STATE,
5017       g_param_spec_enum ("signaling-state", "Signaling State",
5018           "The signaling state of this element",
5019           GST_TYPE_WEBRTC_SIGNALING_STATE,
5020           GST_WEBRTC_SIGNALING_STATE_STABLE,
5021           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5022 
5023   g_object_class_install_property (gobject_class,
5024       PROP_ICE_CONNECTION_STATE,
5025       g_param_spec_enum ("ice-connection-state", "ICE connection state",
5026           "The collective connection state of all ICETransport's",
5027           GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
5028           GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
5029           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5030 
5031   g_object_class_install_property (gobject_class,
5032       PROP_ICE_GATHERING_STATE,
5033       g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
5034           "The collective gathering state of all ICETransport's",
5035           GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
5036           GST_WEBRTC_ICE_GATHERING_STATE_NEW,
5037           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
5038 
5039   g_object_class_install_property (gobject_class,
5040       PROP_BUNDLE_POLICY,
5041       g_param_spec_enum ("bundle-policy", "Bundle Policy",
5042           "The policy to apply for bundling",
5043           GST_TYPE_WEBRTC_BUNDLE_POLICY,
5044           GST_WEBRTC_BUNDLE_POLICY_NONE,
5045           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5046 
5047   g_object_class_install_property (gobject_class,
5048       PROP_ICE_TRANSPORT_POLICY,
5049       g_param_spec_enum ("ice-transport-policy", "ICE Transport Policy",
5050           "The policy to apply for ICE transport",
5051           GST_TYPE_WEBRTC_ICE_TRANSPORT_POLICY,
5052           GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL,
5053           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
5054 
5055   /**
5056    * GstWebRTCBin::create-offer:
5057    * @object: the #GstWebRtcBin
5058    * @options: create-offer options
5059    * @promise: a #GstPromise which will contain the offer
5060    */
5061   gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
5062       g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
5063       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5064       G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL,
5065       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
5066       GST_TYPE_PROMISE);
5067 
5068   /**
5069    * GstWebRTCBin::create-answer:
5070    * @object: the #GstWebRtcBin
5071    * @options: create-answer options
5072    * @promise: a #GstPromise which will contain the answer
5073    */
5074   gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
5075       g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
5076       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5077       G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL,
5078       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
5079       GST_TYPE_PROMISE);
5080 
5081   /**
5082    * GstWebRTCBin::set-local-description:
5083    * @object: the #GstWebRtcBin
5084    * @desc: a #GstWebRTCSessionDescription description
5085    * @promise: (nullable): a #GstPromise to be notified when it's set
5086    */
5087   gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
5088       g_signal_new_class_handler ("set-local-description",
5089       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5090       G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL,
5091       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
5092       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
5093 
5094   /**
5095    * GstWebRTCBin::set-remote-description:
5096    * @object: the #GstWebRtcBin
5097    * @desc: a #GstWebRTCSessionDescription description
5098    * @promise: (nullable): a #GstPromise to be notified when it's set
5099    */
5100   gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
5101       g_signal_new_class_handler ("set-remote-description",
5102       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5103       G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL,
5104       g_cclosure_marshal_generic, G_TYPE_NONE, 2,
5105       GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
5106 
5107   /**
5108    * GstWebRTCBin::add-ice-candidate:
5109    * @object: the #GstWebRtcBin
5110    * @mline_index: the index of the media description in the SDP
5111    * @ice-candidate: an ice candidate
5112    */
5113   gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
5114       g_signal_new_class_handler ("add-ice-candidate",
5115       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5116       G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL,
5117       g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
5118 
5119   /**
5120    * GstWebRTCBin::get-stats:
5121    * @object: the #GstWebRtcBin
5122    * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all
5123    * @promise: a #GstPromise for the result
5124    *
5125    * The @promise will contain the result of retrieving the session statistics.
5126    * The structure will be named 'application/x-webrtc-stats and contain the
5127    * following based on the webrtc-stats spec available from
5128    * https://www.w3.org/TR/webrtc-stats/.  As the webrtc-stats spec is a draft
5129    * and is constantly changing these statistics may be changed to fit with
5130    * the latest spec.
5131    *
5132    * Each field key is a unique identifer for each RTCStats
5133    * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
5134    * GstStructure) in the RTCStatsReport
5135    * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object).  Each supported
5136    * field in the RTCStats subclass is outlined below.
5137    *
5138    * Each statistics structure contains the following values as defined by
5139    * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
5140    *
5141    *  "timestamp"           G_TYPE_DOUBLE               timestamp the statistics were generated
5142    *  "type"                GST_TYPE_WEBRTC_STATS_TYPE  the type of statistics reported
5143    *  "id"                  G_TYPE_STRING               unique identifier
5144    *
5145    * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
5146    *
5147    *  "payload-type"        G_TYPE_UINT                 the rtp payload number in use
5148    *  "clock-rate"          G_TYPE_UINT                 the rtp clock-rate
5149    *
5150    * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
5151    *
5152    *  "ssrc"                G_TYPE_STRING               the rtp sequence src in use
5153    *  "transport-id"        G_TYPE_STRING               identifier for the associated RTCTransportStats for this stream
5154    *  "codec-id"            G_TYPE_STRING               identifier for the associated RTCCodecStats for this stream
5155    *  "fir-count"           G_TYPE_UINT                 FIR requests received by the sender (only for local statistics)
5156    *  "pli-count"           G_TYPE_UINT                 PLI requests received by the sender (only for local statistics)
5157    *  "nack-count"          G_TYPE_UINT                 NACK requests received by the sender (only for local statistics)
5158    *
5159    * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
5160    *
5161    *  "packets-received"     G_TYPE_UINT64              number of packets received (only for local inbound)
5162    *  "bytes-received"       G_TYPE_UINT64              number of bytes received (only for local inbound)
5163    *  "packets-lost"         G_TYPE_UINT                number of packets lost
5164    *  "jitter"               G_TYPE_DOUBLE              packet jitter measured in secondss
5165    *
5166    * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
5167    *
5168    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteOutboundRTPStreamStats
5169    *
5170    * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
5171    *
5172    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCOutboundRTPSTreamStats
5173    *  "round-trip-time"     G_TYPE_DOUBLE               round trip time of packets measured in seconds
5174    *
5175    * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
5176    *
5177    *  "packets-sent"        G_TYPE_UINT64               number of packets sent (only for local outbound)
5178    *  "bytes-sent"          G_TYPE_UINT64               number of packets sent (only for local outbound)
5179    *
5180    * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
5181    *
5182    *  "remote-id"           G_TYPE_STRING               identifier for the associated RTCRemoteInboundRTPSTreamStats
5183    *
5184    * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
5185    *
5186    *  "local-id"            G_TYPE_STRING               identifier for the associated RTCInboundRTPSTreamStats
5187    *
5188    */
5189   gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
5190       g_signal_new_class_handler ("get-stats",
5191       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5192       G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL,
5193       g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_PAD,
5194       GST_TYPE_PROMISE);
5195 
5196   /**
5197    * GstWebRTCBin::on-negotiation-needed:
5198    * @object: the #GstWebRtcBin
5199    */
5200   gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
5201       g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
5202       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5203       G_TYPE_NONE, 0);
5204 
5205   /**
5206    * GstWebRTCBin::on-ice-candidate:
5207    * @object: the #GstWebRtcBin
5208    * @mline_index: the index of the media description in the SDP
5209    * @candidate: the ICE candidate
5210    */
5211   gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
5212       g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
5213       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5214       G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
5215 
5216   /**
5217    * GstWebRTCBin::on-new-transceiver:
5218    * @object: the #GstWebRtcBin
5219    * @candidate: the new #GstWebRTCRTPTransceiver
5220    */
5221   gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] =
5222       g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass),
5223       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5224       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER);
5225 
5226   /**
5227    * GstWebRTCBin::on-data-channel:
5228    * @object: the #GstWebRtcBin
5229    * @candidate: the new #GstWebRTCDataChannel
5230    */
5231   gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] =
5232       g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass),
5233       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
5234       G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL);
5235 
5236   /**
5237    * GstWebRTCBin::add-transceiver:
5238    * @object: the #GstWebRtcBin
5239    * @direction: the direction of the new transceiver
5240    * @caps: (allow none): the codec preferences for this transceiver
5241    *
5242    * Returns: the new #GstWebRTCRTPTransceiver
5243    */
5244   gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
5245       g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
5246       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5247       G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
5248       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
5249       GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
5250 
5251   /**
5252    * GstWebRTCBin::get-transceivers:
5253    * @object: the #GstWebRtcBin
5254    *
5255    * Returns: a #GArray of #GstWebRTCRTPTransceivers
5256    */
5257   gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
5258       g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
5259       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5260       G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL,
5261       g_cclosure_marshal_generic, G_TYPE_ARRAY, 0);
5262 
5263   /**
5264    * GstWebRTCBin::get-transceiver:
5265    * @object: the #GstWebRtcBin
5266    * @idx: The index of the transceiver
5267    *
5268    * Returns: the #GstWebRTCRTPTransceiver, or %NULL
5269    * Since: 1.16
5270    */
5271   gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] =
5272       g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass),
5273       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5274       G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL,
5275       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1,
5276       G_TYPE_INT);
5277 
5278   /**
5279    * GstWebRTCBin::add-turn-server:
5280    * @object: the #GstWebRtcBin
5281    * @uri: The uri of the server of the form turn(s)://username:password@host:port
5282    *
5283    * Add a turn server to obtain ICE candidates from
5284    */
5285   gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] =
5286       g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass),
5287       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5288       G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL,
5289       g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
5290 
5291   /*
5292    * GstWebRTCBin::create-data-channel:
5293    * @object: the #GstWebRtcBin
5294    * @label: the label for the data channel
5295    * @options: a #GstStructure of options for creating the data channel
5296    *
5297    * The options dictionary is the same format as the RTCDataChannelInit
5298    * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and
5299    * and reproduced below
5300    *
5301    *  ordered               G_TYPE_BOOLEAN        Whether the channal will send data with guarenteed ordering
5302    *  max-packet-lifetime   G_TYPE_INT            The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset
5303    *  max-retransmits       G_TYPE_INT            The number of times data will be attempted to be transmitted without acknowledgement before dropping
5304    *  protocol              G_TYPE_STRING         The subprotocol used by this channel
5305    *  negotiated            G_TYPE_BOOLEAN        Whether the created data channel should not perform in-band chnanel announcment.  If %TRUE, then application must negotiate the channel itself and create the corresponding channel on the peer with the same id.
5306    *  id                    G_TYPE_INT            Override the default identifier selection of this channel
5307    *  priority              GST_TYPE_WEBRTC_PRIORITY_TYPE   The priority to use for this channel
5308    *
5309    * Returns: (transfer full): a new data channel object
5310    */
5311   gst_webrtc_bin_signals[CREATE_DATA_CHANNEL_SIGNAL] =
5312       g_signal_new_class_handler ("create-data-channel",
5313       G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
5314       G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL,
5315       g_cclosure_marshal_generic, GST_TYPE_WEBRTC_DATA_CHANNEL, 2,
5316       G_TYPE_STRING, GST_TYPE_STRUCTURE);
5317 }
5318 
5319 static void
_deref_unparent_and_unref(GObject ** object)5320 _deref_unparent_and_unref (GObject ** object)
5321 {
5322   GstObject *obj = GST_OBJECT (*object);
5323 
5324   GST_OBJECT_PARENT (obj) = NULL;
5325 
5326   gst_object_unref (*object);
5327 }
5328 
5329 static void
_transport_free(GObject ** object)5330 _transport_free (GObject ** object)
5331 {
5332   TransportStream *stream = (TransportStream *) * object;
5333   GstWebRTCBin *webrtc;
5334 
5335   webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
5336 
5337   if (stream->transport) {
5338     g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
5339     g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
5340   }
5341   if (stream->rtcp_transport) {
5342     g_signal_handlers_disconnect_by_data (stream->rtcp_transport->transport,
5343         webrtc);
5344     g_signal_handlers_disconnect_by_data (stream->rtcp_transport, webrtc);
5345   }
5346 
5347   gst_object_unref (*object);
5348 }
5349 
5350 static void
gst_webrtc_bin_init(GstWebRTCBin * webrtc)5351 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
5352 {
5353   webrtc->priv = gst_webrtc_bin_get_instance_private (webrtc);
5354   g_mutex_init (PC_GET_LOCK (webrtc));
5355   g_cond_init (PC_GET_COND (webrtc));
5356 
5357   webrtc->rtpbin = _create_rtpbin (webrtc);
5358   gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
5359 
5360   webrtc->priv->transceivers = g_array_new (FALSE, TRUE, sizeof (gpointer));
5361   g_array_set_clear_func (webrtc->priv->transceivers,
5362       (GDestroyNotify) _deref_unparent_and_unref);
5363 
5364   webrtc->priv->transports = g_array_new (FALSE, TRUE, sizeof (gpointer));
5365   g_array_set_clear_func (webrtc->priv->transports,
5366       (GDestroyNotify) _transport_free);
5367 
5368   webrtc->priv->data_channels = g_array_new (FALSE, TRUE, sizeof (gpointer));
5369   g_array_set_clear_func (webrtc->priv->data_channels,
5370       (GDestroyNotify) _deref_and_unref);
5371 
5372   webrtc->priv->pending_data_channels =
5373       g_array_new (FALSE, TRUE, sizeof (gpointer));
5374   g_array_set_clear_func (webrtc->priv->pending_data_channels,
5375       (GDestroyNotify) _deref_and_unref);
5376 
5377   webrtc->priv->session_mid_map =
5378       g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
5379   g_array_set_clear_func (webrtc->priv->session_mid_map,
5380       (GDestroyNotify) clear_session_mid_item);
5381 
5382   webrtc->priv->ice = gst_webrtc_ice_new ();
5383   g_signal_connect (webrtc->priv->ice, "on-ice-candidate",
5384       G_CALLBACK (_on_ice_candidate), webrtc);
5385   webrtc->priv->ice_stream_map =
5386       g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
5387   webrtc->priv->pending_ice_candidates =
5388       g_array_new (FALSE, TRUE, sizeof (IceCandidateItem *));
5389   g_array_set_clear_func (webrtc->priv->pending_ice_candidates,
5390       (GDestroyNotify) _clear_ice_candidate_item);
5391 
5392   /* we start off closed until we move to READY */
5393   webrtc->priv->is_closed = TRUE;
5394 }
5395