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