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