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