2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
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.
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.
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.
24 #include "gstwebrtcbin.h"
25 #include "gstwebrtcstats.h"
26 #include "transportstream.h"
27 #include "transportreceivebin.h"
29 #include "webrtcsdp.h"
30 #include "webrtctransceiver.h"
36 #define RANDOM_SESSION_ID \
37 ((((((guint64) g_random_int()) << 32) | \
38 (guint64) g_random_int ())) & \
39 G_GUINT64_CONSTANT (0x7fffffffffffffff))
41 #define PC_GET_LOCK(w) (&w->priv->pc_lock)
42 #define PC_LOCK(w) (g_mutex_lock (PC_GET_LOCK(w)))
43 #define PC_UNLOCK(w) (g_mutex_unlock (PC_GET_LOCK(w)))
45 #define PC_GET_COND(w) (&w->priv->pc_cond)
46 #define PC_COND_WAIT(w) (g_cond_wait(PC_GET_COND(w), PC_GET_LOCK(w)))
47 #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w)))
48 #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w)))
51 * This webrtcbin implements the majority of the W3's peerconnection API and
52 * implementation guide where possible. Generating offers, answers and setting
53 * local and remote SDP's are all supported. To start with, only the media
54 * interface has been implemented (no datachannel yet).
56 * Each input/output pad is equivalent to a Track in W3 parlance which are
57 * added/removed from the bin. The number of requested sink pads is the number
58 * of streams that will be sent to the receiver and will be associated with a
59 * GstWebRTCRTPTransceiver (very similar to W3 RTPTransceiver's).
61 * On the receiving side, RTPTransceiver's are created in response to setting
62 * a remote description. Output pads for the receiving streams in the set
63 * description are also created.
68 * assert sending payload type matches the stream
69 * reconfiguration (of anything)
72 * setting custom DTLS certificates
75 * seperate session id's from mlineindex properly
76 * how to deal with replacing a input/output track/stream
79 #define GST_CAT_DEFAULT gst_webrtc_bin_debug
80 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
83 gst_webrtc_bin_error_quark (void)
85 return g_quark_from_static_string ("gst-webrtc-bin-error-quark");
88 G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
91 gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
92 const GValue * value, GParamSpec * pspec)
96 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
102 gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
103 GValue * value, GParamSpec * pspec)
107 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
113 gst_webrtc_bin_pad_finalize (GObject * object)
115 GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object);
118 gst_object_unref (pad->trans);
121 G_OBJECT_CLASS (gst_webrtc_bin_pad_parent_class)->finalize (object);
125 gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
127 GObjectClass *gobject_class = (GObjectClass *) klass;
129 gobject_class->get_property = gst_webrtc_bin_pad_get_property;
130 gobject_class->set_property = gst_webrtc_bin_pad_set_property;
131 gobject_class->finalize = gst_webrtc_bin_pad_finalize;
135 _transport_stream_get_caps_for_pt (TransportStream * stream, guint pt)
139 len = stream->ptmap->len;
140 for (i = 0; i < len; i++) {
141 PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i);
149 gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad)
153 static GstWebRTCBinPad *
154 gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction)
156 GstWebRTCBinPad *pad =
157 g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction",
160 if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) {
161 gst_object_unref (pad);
165 GST_DEBUG_OBJECT (pad, "new visible pad with direction %s",
166 direction == GST_PAD_SRC ? "src" : "sink");
170 #define gst_webrtc_bin_parent_class parent_class
171 G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN,
172 GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0,
173 "webrtcbin element");
176 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
179 GST_STATIC_CAPS ("application/x-rtp"));
181 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u",
184 GST_STATIC_CAPS ("application/x-rtp"));
190 CREATE_ANSWER_SIGNAL,
191 SET_LOCAL_DESCRIPTION_SIGNAL,
192 SET_REMOTE_DESCRIPTION_SIGNAL,
193 ADD_ICE_CANDIDATE_SIGNAL,
194 ON_NEGOTIATION_NEEDED_SIGNAL,
195 ON_ICE_CANDIDATE_SIGNAL,
197 ADD_TRANSCEIVER_SIGNAL,
198 GET_TRANSCEIVERS_SIGNAL,
205 PROP_CONNECTION_STATE,
206 PROP_SIGNALING_STATE,
207 PROP_ICE_GATHERING_STATE,
208 PROP_ICE_CONNECTION_STATE,
209 PROP_LOCAL_DESCRIPTION,
210 PROP_CURRENT_LOCAL_DESCRIPTION,
211 PROP_PENDING_LOCAL_DESCRIPTION,
212 PROP_REMOTE_DESCRIPTION,
213 PROP_CURRENT_REMOTE_DESCRIPTION,
214 PROP_PENDING_REMOTE_DESCRIPTION,
219 static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 };
221 static GstWebRTCDTLSTransport *
222 _transceiver_get_transport (GstWebRTCRTPTransceiver * trans)
225 return trans->sender->transport;
226 } else if (trans->receiver) {
227 return trans->receiver->transport;
233 static GstWebRTCDTLSTransport *
234 _transceiver_get_rtcp_transport (GstWebRTCRTPTransceiver * trans)
237 return trans->sender->rtcp_transport;
238 } else if (trans->receiver) {
239 return trans->receiver->rtcp_transport;
248 GstWebRTCICEStream *stream;
251 /* FIXME: locking? */
253 _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id)
257 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
258 IceStreamItem *item =
259 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
261 if (item->session_id == session_id) {
262 GST_TRACE_OBJECT (webrtc, "Found ice stream id %" GST_PTR_FORMAT " for "
263 "session %u", item->stream, session_id);
268 GST_TRACE_OBJECT (webrtc, "No ice stream available for session %u",
274 _add_ice_stream_item (GstWebRTCBin * webrtc, guint session_id,
275 GstWebRTCICEStream * stream)
277 IceStreamItem item = { session_id, stream };
279 GST_TRACE_OBJECT (webrtc, "adding ice stream %" GST_PTR_FORMAT " for "
280 "session %u", stream, session_id);
281 g_array_append_val (webrtc->priv->ice_stream_map, item);
291 clear_session_mid_item (SessionMidItem * item)
296 typedef gboolean (*FindTransceiverFunc) (GstWebRTCRTPTransceiver * p1,
299 static GstWebRTCRTPTransceiver *
300 _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data,
301 FindTransceiverFunc func)
305 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
306 GstWebRTCRTPTransceiver *transceiver =
307 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
310 if (func (transceiver, data))
318 match_for_mid (GstWebRTCRTPTransceiver * trans, const gchar * mid)
320 return g_strcmp0 (trans->mid, mid) == 0;
324 transceiver_match_for_mline (GstWebRTCRTPTransceiver * trans, guint * mline)
326 return trans->mline == *mline;
329 static GstWebRTCRTPTransceiver *
330 _find_transceiver_for_mline (GstWebRTCBin * webrtc, guint mlineindex)
332 GstWebRTCRTPTransceiver *trans;
334 trans = _find_transceiver (webrtc, &mlineindex,
335 (FindTransceiverFunc) transceiver_match_for_mline);
337 GST_TRACE_OBJECT (webrtc,
338 "Found transceiver %" GST_PTR_FORMAT " for mlineindex %u", trans,
344 typedef gboolean (*FindTransportFunc) (TransportStream * p1,
347 static TransportStream *
348 _find_transport (GstWebRTCBin * webrtc, gconstpointer data,
349 FindTransportFunc func)
353 for (i = 0; i < webrtc->priv->transports->len; i++) {
354 TransportStream *stream =
355 g_array_index (webrtc->priv->transports, TransportStream *,
358 if (func (stream, data))
366 match_stream_for_session (TransportStream * trans, guint * session)
368 return trans->session_id == *session;
371 static TransportStream *
372 _find_transport_for_session (GstWebRTCBin * webrtc, guint session_id)
374 TransportStream *stream;
376 stream = _find_transport (webrtc, &session_id,
377 (FindTransportFunc) match_stream_for_session);
379 GST_TRACE_OBJECT (webrtc,
380 "Found transport %" GST_PTR_FORMAT " for session %u", stream, session_id);
385 typedef gboolean (*FindPadFunc) (GstWebRTCBinPad * p1, gconstpointer data);
387 static GstWebRTCBinPad *
388 _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func)
390 GstElement *element = GST_ELEMENT (webrtc);
393 GST_OBJECT_LOCK (webrtc);
395 for (; l; l = g_list_next (l)) {
396 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
398 if (func (l->data, data)) {
399 gst_object_ref (l->data);
400 GST_OBJECT_UNLOCK (webrtc);
405 l = webrtc->priv->pending_pads;
406 for (; l; l = g_list_next (l)) {
407 if (!GST_IS_WEBRTC_BIN_PAD (l->data))
409 if (func (l->data, data)) {
410 gst_object_ref (l->data);
411 GST_OBJECT_UNLOCK (webrtc);
415 GST_OBJECT_UNLOCK (webrtc);
421 _add_pad_to_list (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
423 GST_OBJECT_LOCK (webrtc);
424 webrtc->priv->pending_pads = g_list_prepend (webrtc->priv->pending_pads, pad);
425 GST_OBJECT_UNLOCK (webrtc);
429 _remove_pending_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
431 GST_OBJECT_LOCK (webrtc);
432 webrtc->priv->pending_pads = g_list_remove (webrtc->priv->pending_pads, pad);
433 GST_OBJECT_UNLOCK (webrtc);
437 _add_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
439 _remove_pending_pad (webrtc, pad);
441 if (webrtc->priv->running)
442 gst_pad_set_active (GST_PAD (pad), TRUE);
443 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
447 _remove_pad (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
449 _remove_pending_pad (webrtc, pad);
451 gst_element_remove_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
456 GstPadDirection direction;
461 pad_match_for_mline (GstWebRTCBinPad * pad, const MLineMatch * match)
463 return GST_PAD_DIRECTION (pad) == match->direction
464 && pad->mlineindex == match->mlineindex;
467 static GstWebRTCBinPad *
468 _find_pad_for_mline (GstWebRTCBin * webrtc, GstPadDirection direction,
471 MLineMatch m = { direction, mlineindex };
473 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_mline);
478 GstPadDirection direction;
479 GstWebRTCRTPTransceiver *trans;
483 pad_match_for_transceiver (GstWebRTCBinPad * pad, TransMatch * m)
485 return GST_PAD_DIRECTION (pad) == m->direction && pad->trans == m->trans;
488 static GstWebRTCBinPad *
489 _find_pad_for_transceiver (GstWebRTCBin * webrtc, GstPadDirection direction,
490 GstWebRTCRTPTransceiver * trans)
492 TransMatch m = { direction, trans };
494 return _find_pad (webrtc, &m, (FindPadFunc) pad_match_for_transceiver);
499 match_for_ssrc (GstWebRTCBinPad * pad, guint * ssrc)
501 return pad->ssrc == *ssrc;
505 match_for_pad (GstWebRTCBinPad * pad, GstWebRTCBinPad * other)
512 _unlock_pc_thread (GMutex * lock)
514 g_mutex_unlock (lock);
515 return G_SOURCE_REMOVE;
519 _gst_pc_thread (GstWebRTCBin * webrtc)
522 webrtc->priv->main_context = g_main_context_new ();
523 webrtc->priv->loop = g_main_loop_new (webrtc->priv->main_context, FALSE);
525 PC_COND_BROADCAST (webrtc);
526 g_main_context_invoke (webrtc->priv->main_context,
527 (GSourceFunc) _unlock_pc_thread, PC_GET_LOCK (webrtc));
529 /* Having the thread be the thread default GMainContext will break the
530 * required queue-like ordering (from W3's peerconnection spec) of re-entrant
532 g_main_loop_run (webrtc->priv->loop);
535 g_main_context_unref (webrtc->priv->main_context);
536 webrtc->priv->main_context = NULL;
537 g_main_loop_unref (webrtc->priv->loop);
538 webrtc->priv->loop = NULL;
539 PC_COND_BROADCAST (webrtc);
546 _start_thread (GstWebRTCBin * webrtc)
549 webrtc->priv->thread = g_thread_new ("gst-pc-ops",
550 (GThreadFunc) _gst_pc_thread, webrtc);
552 while (!webrtc->priv->loop)
553 PC_COND_WAIT (webrtc);
554 webrtc->priv->is_closed = FALSE;
559 _stop_thread (GstWebRTCBin * webrtc)
562 webrtc->priv->is_closed = TRUE;
563 g_main_loop_quit (webrtc->priv->loop);
564 while (webrtc->priv->loop)
565 PC_COND_WAIT (webrtc);
568 g_thread_unref (webrtc->priv->thread);
572 _execute_op (GstWebRTCBinTask * op)
574 PC_LOCK (op->webrtc);
575 if (op->webrtc->priv->is_closed) {
576 GST_DEBUG_OBJECT (op->webrtc,
577 "Peerconnection is closed, aborting execution");
581 op->op (op->webrtc, op->data);
584 PC_UNLOCK (op->webrtc);
585 return G_SOURCE_REMOVE;
589 _free_op (GstWebRTCBinTask * op)
592 op->notify (op->data);
597 gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
598 gpointer data, GDestroyNotify notify)
600 GstWebRTCBinTask *op;
603 g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc));
605 if (webrtc->priv->is_closed) {
606 GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
611 op = g_new0 (GstWebRTCBinTask, 1);
617 source = g_idle_source_new ();
618 g_source_set_priority (source, G_PRIORITY_DEFAULT);
619 g_source_set_callback (source, (GSourceFunc) _execute_op, op,
620 (GDestroyNotify) _free_op);
621 g_source_attach (source, webrtc->priv->main_context);
622 g_source_unref (source);
625 /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */
626 static GstWebRTCICEConnectionState
627 _collate_ice_connection_states (GstWebRTCBin * webrtc)
629 #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val
630 GstWebRTCICEConnectionState any_state = 0;
631 gboolean all_closed = TRUE;
634 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
635 GstWebRTCRTPTransceiver *rtp_trans =
636 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
638 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
639 TransportStream *stream = trans->stream;
640 GstWebRTCICETransport *transport, *rtcp_transport;
641 GstWebRTCICEConnectionState ice_state;
642 gboolean rtcp_mux = FALSE;
644 if (rtp_trans->stopped)
649 g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
651 transport = _transceiver_get_transport (rtp_trans)->transport;
653 /* get transport state */
654 g_object_get (transport, "state", &ice_state, NULL);
655 any_state |= (1 << ice_state);
656 if (ice_state != STATE (CLOSED))
659 rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans)->transport;
661 if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) {
662 g_object_get (rtcp_transport, "state", &ice_state, NULL);
663 any_state |= (1 << ice_state);
664 if (ice_state != STATE (CLOSED))
669 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x", any_state);
671 if (webrtc->priv->is_closed) {
672 GST_TRACE_OBJECT (webrtc, "returning closed");
673 return STATE (CLOSED);
675 /* Any of the RTCIceTransport s are in the failed state. */
676 if (any_state & (1 << STATE (FAILED))) {
677 GST_TRACE_OBJECT (webrtc, "returning failed");
678 return STATE (FAILED);
680 /* Any of the RTCIceTransport s are in the disconnected state and
681 * none of them are in the failed state. */
682 if (any_state & (1 << STATE (DISCONNECTED))) {
683 GST_TRACE_OBJECT (webrtc, "returning disconnected");
684 return STATE (DISCONNECTED);
686 /* Any of the RTCIceTransport's are in the checking state and none of them
687 * are in the failed or disconnected state. */
688 if (any_state & (1 << STATE (CHECKING))) {
689 GST_TRACE_OBJECT (webrtc, "returning checking");
690 return STATE (CHECKING);
692 /* Any of the RTCIceTransport s are in the new state and none of them are
693 * in the checking, failed or disconnected state, or all RTCIceTransport's
694 * are in the closed state. */
695 if ((any_state & (1 << STATE (NEW))) || all_closed) {
696 GST_TRACE_OBJECT (webrtc, "returning new");
699 /* All RTCIceTransport s are in the connected, completed or closed state
700 * and at least one of them is in the connected state. */
701 if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 <<
702 STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) {
703 GST_TRACE_OBJECT (webrtc, "returning connected");
704 return STATE (CONNECTED);
706 /* All RTCIceTransport s are in the completed or closed state and at least
707 * one of them is in the completed state. */
708 if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED))
709 && any_state & (1 << STATE (COMPLETED))) {
710 GST_TRACE_OBJECT (webrtc, "returning connected");
711 return STATE (CONNECTED);
714 GST_FIXME ("unspecified situation, returning new");
719 /* https://www.w3.org/TR/webrtc/#dom-rtcicegatheringstate */
720 static GstWebRTCICEGatheringState
721 _collate_ice_gathering_states (GstWebRTCBin * webrtc)
723 #define STATE(val) GST_WEBRTC_ICE_GATHERING_STATE_ ## val
724 GstWebRTCICEGatheringState any_state = 0;
725 gboolean all_completed = webrtc->priv->transceivers->len > 0;
728 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
729 GstWebRTCRTPTransceiver *rtp_trans =
730 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
732 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
733 TransportStream *stream = trans->stream;
734 GstWebRTCICETransport *transport, *rtcp_transport;
735 GstWebRTCICEGatheringState ice_state;
736 gboolean rtcp_mux = FALSE;
738 if (rtp_trans->stopped)
743 g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
745 transport = _transceiver_get_transport (rtp_trans)->transport;
747 /* get gathering state */
748 g_object_get (transport, "gathering-state", &ice_state, NULL);
749 any_state |= (1 << ice_state);
750 if (ice_state != STATE (COMPLETE))
751 all_completed = FALSE;
753 rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans)->transport;
755 if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
756 g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL);
757 any_state |= (1 << ice_state);
758 if (ice_state != STATE (COMPLETE))
759 all_completed = FALSE;
763 GST_TRACE_OBJECT (webrtc, "ICE gathering state: 0x%x", any_state);
765 /* Any of the RTCIceTransport s are in the gathering state. */
766 if (any_state & (1 << STATE (GATHERING))) {
767 GST_TRACE_OBJECT (webrtc, "returning gathering");
768 return STATE (GATHERING);
770 /* At least one RTCIceTransport exists, and all RTCIceTransport s are in
771 * the completed gathering state. */
773 GST_TRACE_OBJECT (webrtc, "returning complete");
774 return STATE (COMPLETE);
777 /* Any of the RTCIceTransport s are in the new gathering state and none
778 * of the transports are in the gathering state, or there are no transports. */
779 GST_TRACE_OBJECT (webrtc, "returning new");
784 /* https://www.w3.org/TR/webrtc/#rtcpeerconnectionstate-enum */
785 static GstWebRTCPeerConnectionState
786 _collate_peer_connection_states (GstWebRTCBin * webrtc)
788 #define STATE(v) GST_WEBRTC_PEER_CONNECTION_STATE_ ## v
789 #define ICE_STATE(v) GST_WEBRTC_ICE_CONNECTION_STATE_ ## v
790 #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v
791 GstWebRTCICEConnectionState any_ice_state = 0;
792 GstWebRTCDTLSTransportState any_dtls_state = 0;
795 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
796 GstWebRTCRTPTransceiver *rtp_trans =
797 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
799 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
800 TransportStream *stream = trans->stream;
801 GstWebRTCDTLSTransport *transport, *rtcp_transport;
802 GstWebRTCICEGatheringState ice_state;
803 GstWebRTCDTLSTransportState dtls_state;
804 gboolean rtcp_mux = FALSE;
806 if (rtp_trans->stopped)
811 g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL);
812 transport = _transceiver_get_transport (rtp_trans);
814 /* get transport state */
815 g_object_get (transport, "state", &dtls_state, NULL);
816 any_dtls_state |= (1 << dtls_state);
817 g_object_get (transport->transport, "state", &ice_state, NULL);
818 any_ice_state |= (1 << ice_state);
820 rtcp_transport = _transceiver_get_rtcp_transport (rtp_trans);
822 if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) {
823 g_object_get (rtcp_transport, "state", &dtls_state, NULL);
824 any_dtls_state |= (1 << dtls_state);
825 g_object_get (rtcp_transport->transport, "state", &ice_state, NULL);
826 any_ice_state |= (1 << ice_state);
830 GST_TRACE_OBJECT (webrtc, "ICE connection state: 0x%x. DTLS connection "
831 "state: 0x%x", any_ice_state, any_dtls_state);
833 /* The RTCPeerConnection object's [[ isClosed]] slot is true. */
834 if (webrtc->priv->is_closed) {
835 GST_TRACE_OBJECT (webrtc, "returning closed");
836 return STATE (CLOSED);
839 /* Any of the RTCIceTransport s or RTCDtlsTransport s are in a failed state. */
840 if (any_ice_state & (1 << ICE_STATE (FAILED))) {
841 GST_TRACE_OBJECT (webrtc, "returning failed");
842 return STATE (FAILED);
844 if (any_dtls_state & (1 << DTLS_STATE (FAILED))) {
845 GST_TRACE_OBJECT (webrtc, "returning failed");
846 return STATE (FAILED);
849 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting
850 * or checking state and none of them is in the failed state. */
851 if (any_ice_state & (1 << ICE_STATE (CHECKING))) {
852 GST_TRACE_OBJECT (webrtc, "returning connecting");
853 return STATE (CONNECTING);
855 if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) {
856 GST_TRACE_OBJECT (webrtc, "returning connecting");
857 return STATE (CONNECTING);
860 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected
861 * state and none of them are in the failed or connecting or checking state. */
862 if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) {
863 GST_TRACE_OBJECT (webrtc, "returning disconnected");
864 return STATE (DISCONNECTED);
867 /* All RTCIceTransport's and RTCDtlsTransport's are in the connected,
868 * completed or closed state and at least of them is in the connected or
869 * completed state. */
870 if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 <<
871 ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED)))
872 && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 <<
873 DTLS_STATE (CLOSED)))
874 && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 <<
875 ICE_STATE (COMPLETED))
876 || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) {
877 GST_TRACE_OBJECT (webrtc, "returning connected");
878 return STATE (CONNECTED);
881 /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state
882 * and none of the transports are in the connecting, checking, failed or
883 * disconnected state, or all transports are in the closed state. */
884 if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) {
885 GST_TRACE_OBJECT (webrtc, "returning new");
888 if ((any_ice_state & (1 << ICE_STATE (NEW))
889 || any_dtls_state & (1 << DTLS_STATE (NEW)))
890 && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED)
891 | (1 << ICE_STATE (DISCONNECTED))))
892 && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 <<
893 DTLS_STATE (FAILED)))) {
894 GST_TRACE_OBJECT (webrtc, "returning new");
898 GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new");
906 _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data)
908 GstWebRTCICEGatheringState old_state = webrtc->ice_gathering_state;
909 GstWebRTCICEGatheringState new_state;
911 new_state = _collate_ice_gathering_states (webrtc);
913 if (new_state != webrtc->ice_gathering_state) {
914 gchar *old_s, *new_s;
916 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
918 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
920 GST_INFO_OBJECT (webrtc, "ICE gathering state change from %s(%u) to %s(%u)",
921 old_s, old_state, new_s, new_state);
925 webrtc->ice_gathering_state = new_state;
927 g_object_notify (G_OBJECT (webrtc), "ice-gathering-state");
933 _update_ice_gathering_state (GstWebRTCBin * webrtc)
935 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL,
940 _update_ice_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
942 GstWebRTCICEConnectionState old_state = webrtc->ice_connection_state;
943 GstWebRTCICEConnectionState new_state;
945 new_state = _collate_ice_connection_states (webrtc);
947 if (new_state != old_state) {
948 gchar *old_s, *new_s;
950 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
952 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
954 GST_INFO_OBJECT (webrtc,
955 "ICE connection state change from %s(%u) to %s(%u)", old_s, old_state,
960 webrtc->ice_connection_state = new_state;
962 g_object_notify (G_OBJECT (webrtc), "ice-connection-state");
968 _update_ice_connection_state (GstWebRTCBin * webrtc)
970 gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL,
975 _update_peer_connection_state_task (GstWebRTCBin * webrtc, gpointer data)
977 GstWebRTCPeerConnectionState old_state = webrtc->peer_connection_state;
978 GstWebRTCPeerConnectionState new_state;
980 new_state = _collate_peer_connection_states (webrtc);
982 if (new_state != old_state) {
983 gchar *old_s, *new_s;
985 old_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
987 new_s = _enum_value_to_string (GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
989 GST_INFO_OBJECT (webrtc,
990 "Peer connection state change from %s(%u) to %s(%u)", old_s, old_state,
995 webrtc->peer_connection_state = new_state;
997 g_object_notify (G_OBJECT (webrtc), "connection-state");
1003 _update_peer_connection_state (GstWebRTCBin * webrtc)
1005 gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task,
1009 /* http://w3c.github.io/webrtc-pc/#dfn-check-if-negotiation-is-needed */
1011 _check_if_negotiation_is_needed (GstWebRTCBin * webrtc)
1015 GST_LOG_OBJECT (webrtc, "checking if negotiation is needed");
1017 /* If any implementation-specific negotiation is required, as described at
1018 * the start of this section, return "true".
1020 /* FIXME: emit when input caps/format changes? */
1022 /* If connection has created any RTCDataChannel's, and no m= section has
1023 * been negotiated yet for data, return "true".
1026 if (!webrtc->current_local_description) {
1027 GST_LOG_OBJECT (webrtc, "no local description set");
1031 if (!webrtc->current_remote_description) {
1032 GST_LOG_OBJECT (webrtc, "no remote description set");
1036 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1037 GstWebRTCRTPTransceiver *trans;
1040 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1043 if (trans->stopped) {
1044 /* FIXME: If t is stopped and is associated with an m= section according to
1045 * [JSEP] (section 3.4.1.), but the associated m= section is not yet
1046 * rejected in connection's currentLocalDescription or
1047 * currentRemoteDescription , return "true". */
1048 GST_FIXME_OBJECT (webrtc,
1049 "check if the transceiver is rejected in descriptions");
1051 const GstSDPMedia *media;
1052 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1054 if (trans->mline == -1) {
1055 GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT,
1059 /* internal inconsistency */
1060 g_assert (trans->mline <
1061 gst_sdp_message_medias_len (webrtc->current_local_description->sdp));
1062 g_assert (trans->mline <
1063 gst_sdp_message_medias_len (webrtc->current_remote_description->sdp));
1065 /* FIXME: msid handling
1066 * If t's direction is "sendrecv" or "sendonly", and the associated m=
1067 * section in connection's currentLocalDescription doesn't contain an
1068 * "a=msid" line, return "true". */
1071 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
1073 local_dir = _get_direction_from_media (media);
1076 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
1078 remote_dir = _get_direction_from_media (media);
1080 if (webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) {
1081 /* If connection's currentLocalDescription if of type "offer", and
1082 * the direction of the associated m= section in neither the offer
1083 * nor answer matches t's direction, return "true". */
1085 if (local_dir != trans->direction && remote_dir != trans->direction) {
1086 GST_LOG_OBJECT (webrtc,
1087 "transceiver direction doesn't match description");
1090 } else if (webrtc->current_local_description->type ==
1091 GST_WEBRTC_SDP_TYPE_ANSWER) {
1092 GstWebRTCRTPTransceiverDirection intersect_dir;
1094 /* If connection's currentLocalDescription if of type "answer", and
1095 * the direction of the associated m= section in the answer does not
1096 * match t's direction intersected with the offered direction (as
1097 * described in [JSEP] (section 5.3.1.)), return "true". */
1099 /* remote is the offer, local is the answer */
1100 intersect_dir = _intersect_answer_directions (remote_dir, local_dir);
1102 if (intersect_dir != trans->direction) {
1103 GST_LOG_OBJECT (webrtc,
1104 "transceiver direction doesn't match description");
1111 GST_LOG_OBJECT (webrtc, "no negotiation needed");
1116 _check_need_negotiation_task (GstWebRTCBin * webrtc, gpointer unused)
1118 if (webrtc->priv->need_negotiation) {
1119 GST_TRACE_OBJECT (webrtc, "emitting on-negotiation-needed");
1121 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL],
1127 /* http://w3c.github.io/webrtc-pc/#dfn-update-the-negotiation-needed-flag */
1129 _update_need_negotiation (GstWebRTCBin * webrtc)
1131 /* If connection's [[isClosed]] slot is true, abort these steps. */
1132 if (webrtc->priv->is_closed)
1134 /* If connection's signaling state is not "stable", abort these steps. */
1135 if (webrtc->signaling_state != GST_WEBRTC_SIGNALING_STATE_STABLE)
1138 /* If the result of checking if negotiation is needed is "false", clear the
1139 * negotiation-needed flag by setting connection's [[ needNegotiation]] slot
1140 * to false, and abort these steps. */
1141 if (!_check_if_negotiation_is_needed (webrtc)) {
1142 webrtc->priv->need_negotiation = FALSE;
1145 /* If connection's [[needNegotiation]] slot is already true, abort these steps. */
1146 if (webrtc->priv->need_negotiation)
1148 /* Set connection's [[needNegotiation]] slot to true. */
1149 webrtc->priv->need_negotiation = TRUE;
1150 /* Queue a task to check connection's [[ needNegotiation]] slot and, if still
1151 * true, fire a simple event named negotiationneeded at connection. */
1152 gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL,
1157 _find_codec_preferences (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * trans,
1158 GstPadDirection direction, guint media_idx)
1160 GstCaps *ret = NULL;
1162 GST_LOG_OBJECT (webrtc, "retreiving codec preferences from %" GST_PTR_FORMAT,
1165 if (trans->codec_preferences) {
1166 GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT,
1167 trans->codec_preferences);
1168 ret = gst_caps_ref (trans->codec_preferences);
1170 GstWebRTCBinPad *pad = _find_pad_for_mline (webrtc, direction, media_idx);
1172 GstCaps *caps = gst_pad_get_current_caps (GST_PAD (pad));
1174 GST_LOG_OBJECT (webrtc, "Using current pad caps: %" GST_PTR_FORMAT,
1177 if ((caps = gst_pad_peer_query_caps (GST_PAD (pad), NULL)))
1178 GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT,
1183 gst_object_unref (pad);
1191 _add_supported_attributes_to_caps (const GstCaps * caps)
1196 ret = gst_caps_make_writable (caps);
1198 for (i = 0; i < gst_caps_get_size (ret); i++) {
1199 GstStructure *s = gst_caps_get_structure (ret, i);
1201 if (!gst_structure_has_field (s, "rtcp-fb-nack"))
1202 gst_structure_set (s, "rtcp-fb-nack", G_TYPE_BOOLEAN, TRUE, NULL);
1203 if (!gst_structure_has_field (s, "rtcp-fb-nack-pli"))
1204 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL);
1205 /* FIXME: is this needed? */
1206 /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc"))
1207 gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */
1209 /* FIXME: codec-specific paramters? */
1216 _on_ice_transport_notify_state (GstWebRTCICETransport * transport,
1217 GParamSpec * pspec, GstWebRTCBin * webrtc)
1219 _update_ice_connection_state (webrtc);
1220 _update_peer_connection_state (webrtc);
1224 _on_ice_transport_notify_gathering_state (GstWebRTCICETransport * transport,
1225 GParamSpec * pspec, GstWebRTCBin * webrtc)
1227 _update_ice_gathering_state (webrtc);
1231 _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
1232 GParamSpec * pspec, GstWebRTCBin * webrtc)
1234 _update_peer_connection_state (webrtc);
1237 static WebRTCTransceiver *
1238 _create_webrtc_transceiver (GstWebRTCBin * webrtc)
1240 WebRTCTransceiver *trans;
1241 GstWebRTCRTPTransceiver *rtp_trans;
1242 GstWebRTCRTPSender *sender;
1243 GstWebRTCRTPReceiver *receiver;
1245 sender = gst_webrtc_rtp_sender_new (NULL);
1246 receiver = gst_webrtc_rtp_receiver_new ();
1247 trans = webrtc_transceiver_new (webrtc, sender, receiver);
1248 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1249 rtp_trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
1250 rtp_trans->mline = -1;
1252 g_array_append_val (webrtc->priv->transceivers, trans);
1254 gst_object_unref (sender);
1255 gst_object_unref (receiver);
1260 static TransportStream *
1261 _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
1263 GstWebRTCDTLSTransport *transport;
1264 TransportStream *ret;
1267 /* FIXME: how to parametrize the sender and the receiver */
1268 ret = transport_stream_new (webrtc, session_id);
1269 transport = ret->transport;
1271 g_signal_connect (G_OBJECT (transport->transport), "notify::state",
1272 G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1273 g_signal_connect (G_OBJECT (transport->transport),
1274 "notify::gathering-state",
1275 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1276 g_signal_connect (G_OBJECT (transport), "notify::state",
1277 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1279 if ((transport = ret->rtcp_transport)) {
1280 g_signal_connect (G_OBJECT (transport->transport),
1281 "notify::state", G_CALLBACK (_on_ice_transport_notify_state), webrtc);
1282 g_signal_connect (G_OBJECT (transport->transport),
1283 "notify::gathering-state",
1284 G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
1285 g_signal_connect (G_OBJECT (transport), "notify::state",
1286 G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
1289 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin));
1290 gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin));
1292 pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id);
1293 if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src",
1294 GST_ELEMENT (webrtc->rtpbin), pad_name))
1295 g_warn_if_reached ();
1298 pad_name = g_strdup_printf ("send_rtcp_src_%u", ret->session_id);
1299 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1300 GST_ELEMENT (ret->send_bin), "rtcp_sink"))
1301 g_warn_if_reached ();
1304 g_array_append_val (webrtc->priv->transports, ret);
1306 GST_TRACE_OBJECT (webrtc,
1307 "Create transport %" GST_PTR_FORMAT " for session %u", ret, session_id);
1309 gst_element_sync_state_with_parent (GST_ELEMENT (ret->send_bin));
1310 gst_element_sync_state_with_parent (GST_ELEMENT (ret->receive_bin));
1315 /* based off https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-18#section-5.2.1 */
1317 sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media,
1318 GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx)
1321 * rtp header extensions
1328 * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05
1330 gchar *direction, *sdp_mid;
1334 /* "An m= section is generated for each RtpTransceiver that has been added
1335 * to the Bin, excluding any stopped RtpTransceivers." */
1338 if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
1339 || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE)
1342 gst_sdp_media_set_port_info (media, 9, 0);
1343 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
1344 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
1347 _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION,
1349 gst_sdp_media_add_attribute (media, direction, "");
1351 /* FIXME: negotiate this */
1352 gst_sdp_media_add_attribute (media, "rtcp-mux", "");
1353 gst_sdp_media_add_attribute (media, "rtcp-rsize", NULL);
1355 if (type == GST_WEBRTC_SDP_TYPE_OFFER) {
1356 caps = _find_codec_preferences (webrtc, trans, GST_PAD_SINK, media_idx);
1357 caps = _add_supported_attributes_to_caps (caps);
1358 } else if (type == GST_WEBRTC_SDP_TYPE_ANSWER) {
1359 caps = _find_codec_preferences (webrtc, trans, GST_PAD_SRC, media_idx);
1360 /* FIXME: add rtcp-fb paramaters */
1362 g_assert_not_reached ();
1365 if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
1366 GST_WARNING_OBJECT (webrtc, "no caps available for transceiver, skipping");
1368 gst_caps_unref (caps);
1372 for (i = 0; i < gst_caps_get_size (caps); i++) {
1373 GstCaps *format = gst_caps_new_empty ();
1374 const GstStructure *s = gst_caps_get_structure (caps, i);
1376 gst_caps_append_structure (format, gst_structure_copy (s));
1378 GST_DEBUG_OBJECT (webrtc, "Adding %u-th caps %" GST_PTR_FORMAT
1379 " to %u-th media", i, format, media_idx);
1381 /* this only looks at the first structure so we loop over the given caps
1382 * and add each structure inside it piecemeal */
1383 gst_sdp_media_set_media_from_caps (format, media);
1385 gst_caps_unref (format);
1388 /* Some identifier; we also add the media name to it so it's identifiable */
1389 sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media),
1390 webrtc->priv->media_counter++);
1391 gst_sdp_media_add_attribute (media, "mid", sdp_mid);
1394 if (trans->sender) {
1395 gchar *cert, *fingerprint, *val;
1397 if (!trans->sender->transport) {
1398 TransportStream *item;
1400 item = _find_transport_for_session (webrtc, media_idx);
1402 item = _create_transport_channel (webrtc, media_idx);
1403 webrtc_transceiver_set_transport (WEBRTC_TRANSCEIVER (trans), item);
1406 g_object_get (trans->sender->transport, "certificate", &cert, NULL);
1409 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
1412 g_strdup_printf ("%s %s",
1413 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
1414 g_free (fingerprint);
1416 gst_sdp_media_add_attribute (media, "fingerprint", val);
1420 gst_caps_unref (caps);
1425 static GstSDPMessage *
1426 _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options)
1431 gst_sdp_message_new (&ret);
1433 gst_sdp_message_set_version (ret, "0");
1435 /* FIXME: session id and version need special handling depending on the state we're in */
1436 gchar *sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID);
1437 gst_sdp_message_set_origin (ret, "-", sess_id, "0", "IN", "IP4", "0.0.0.0");
1440 gst_sdp_message_set_session_name (ret, "-");
1441 gst_sdp_message_add_time (ret, "0", "0", NULL);
1442 gst_sdp_message_add_attribute (ret, "ice-options", "trickle");
1444 /* for each rtp transceiver */
1445 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
1446 GstWebRTCRTPTransceiver *trans;
1447 GstSDPMedia media = { 0, };
1451 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1454 gst_sdp_media_init (&media);
1455 /* mandated by JSEP */
1456 gst_sdp_media_add_attribute (&media, "setup", "actpass");
1458 /* FIXME: only needed when restarting ICE */
1459 _generate_ice_credentials (&ufrag, &pwd);
1460 gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag);
1461 gst_sdp_media_add_attribute (&media, "ice-pwd", pwd);
1465 if (sdp_media_from_transceiver (webrtc, &media, trans,
1466 GST_WEBRTC_SDP_TYPE_OFFER, i))
1467 gst_sdp_message_add_media (ret, &media);
1469 gst_sdp_media_uninit (&media);
1472 /* FIXME: pre-emptively setup receiving elements when needed */
1474 /* XXX: only true for the initial offerer */
1475 g_object_set (webrtc->priv->ice, "controller", TRUE, NULL);
1480 static GstSDPMessage *
1481 _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options)
1483 GstSDPMessage *ret = NULL;
1484 const GstWebRTCSessionDescription *pending_remote =
1485 webrtc->pending_remote_description;
1488 if (!webrtc->pending_remote_description) {
1489 GST_ERROR_OBJECT (webrtc,
1490 "Asked to create an answer without a remote description");
1494 gst_sdp_message_new (&ret);
1496 /* FIXME: session id and version need special handling depending on the state we're in */
1497 gst_sdp_message_set_version (ret, "0");
1499 const GstSDPOrigin *offer_origin =
1500 gst_sdp_message_get_origin (pending_remote->sdp);
1501 gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id, "0", "IN",
1504 gst_sdp_message_set_session_name (ret, "-");
1506 for (i = 0; i < gst_sdp_message_attributes_len (pending_remote->sdp); i++) {
1507 const GstSDPAttribute *attr =
1508 gst_sdp_message_get_attribute (pending_remote->sdp, i);
1510 if (g_strcmp0 (attr->key, "ice-options") == 0) {
1511 gst_sdp_message_add_attribute (ret, attr->key, attr->value);
1515 for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) {
1519 GstSDPMedia *media = NULL;
1520 GstSDPMedia *offer_media;
1521 GstWebRTCRTPTransceiver *rtp_trans = NULL;
1522 WebRTCTransceiver *trans = NULL;
1523 GstWebRTCRTPTransceiverDirection offer_dir, answer_dir;
1524 GstWebRTCDTLSSetup offer_setup, answer_setup;
1525 GstCaps *offer_caps, *answer_caps = NULL;
1529 gst_sdp_media_new (&media);
1530 gst_sdp_media_set_port_info (media, 9, 0);
1531 gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF");
1532 gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0);
1535 /* FIXME: only needed when restarting ICE */
1537 _generate_ice_credentials (&ufrag, &pwd);
1538 gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag);
1539 gst_sdp_media_add_attribute (media, "ice-pwd", pwd);
1545 (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i);
1546 for (j = 0; j < gst_sdp_media_attributes_len (offer_media); j++) {
1547 const GstSDPAttribute *attr =
1548 gst_sdp_media_get_attribute (offer_media, j);
1550 if (g_strcmp0 (attr->key, "mid") == 0
1551 || g_strcmp0 (attr->key, "rtcp-mux") == 0) {
1552 gst_sdp_media_add_attribute (media, attr->key, attr->value);
1553 /* FIXME: handle anything we want to keep */
1557 offer_caps = gst_caps_new_empty ();
1558 for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) {
1559 guint pt = atoi (gst_sdp_media_get_format (offer_media, j));
1563 caps = gst_sdp_media_get_caps_from_media (offer_media, pt);
1565 /* gst_sdp_media_get_caps_from_media() produces caps with name
1566 * "application/x-unknown" which will fail intersection with
1567 * "application/x-rtp" caps so mangle the returns caps to have the
1568 * correct name here */
1569 for (k = 0; k < gst_caps_get_size (caps); k++) {
1570 GstStructure *s = gst_caps_get_structure (caps, k);
1571 gst_structure_set_name (s, "application/x-rtp");
1574 gst_caps_append (offer_caps, caps);
1577 for (j = 0; j < webrtc->priv->transceivers->len; j++) {
1578 GstCaps *trans_caps;
1581 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
1583 trans_caps = _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i);
1585 GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT
1586 " and %" GST_PTR_FORMAT, offer_caps, trans_caps);
1588 /* FIXME: technically this is a little overreaching as some fields we
1589 * we can deal with not having and/or we may have unrecognized fields
1590 * that we cannot actually support */
1592 answer_caps = gst_caps_intersect (offer_caps, trans_caps);
1593 if (answer_caps && !gst_caps_is_empty (answer_caps)) {
1594 GST_LOG_OBJECT (webrtc,
1595 "found compatible transceiver %" GST_PTR_FORMAT
1596 " for offer media %u", trans, i);
1598 gst_caps_unref (trans_caps);
1602 gst_caps_unref (answer_caps);
1606 gst_caps_unref (trans_caps);
1615 answer_dir = rtp_trans->direction;
1619 /* if no transceiver, then we only receive that stream and respond with
1620 * the exact same caps */
1621 /* FIXME: how to validate that subsequent elements can actually receive
1622 * this payload/format */
1623 answer_dir = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY;
1624 answer_caps = gst_caps_ref (offer_caps);
1626 /* respond with the requested caps */
1628 gst_sdp_media_set_media_from_caps (answer_caps, media);
1629 gst_caps_unref (answer_caps);
1633 trans = _create_webrtc_transceiver (webrtc);
1634 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
1635 rtp_trans->direction = answer_dir;
1636 rtp_trans->mline = i;
1638 trans = WEBRTC_TRANSCEIVER (rtp_trans);
1641 /* set the new media direction */
1642 offer_dir = _get_direction_from_media (offer_media);
1643 answer_dir = _intersect_answer_directions (offer_dir, answer_dir);
1644 if (answer_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE) {
1645 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
1646 "transceiver direction");
1649 _media_replace_direction (media, answer_dir);
1651 /* set the a=setup: attribute */
1652 offer_setup = _get_dtls_setup_from_media (offer_media);
1653 answer_setup = _intersect_dtls_setup (offer_setup);
1654 if (answer_setup == GST_WEBRTC_DTLS_SETUP_NONE) {
1655 GST_WARNING_OBJECT (webrtc, "Could not intersect offer direction with "
1656 "transceiver direction");
1659 _media_replace_setup (media, answer_setup);
1661 /* FIXME: bundle! */
1662 if (!trans->stream) {
1663 TransportStream *item = _find_transport_for_session (webrtc, i);
1665 item = _create_transport_channel (webrtc, i);
1666 webrtc_transceiver_set_transport (trans, item);
1668 /* set the a=fingerprint: for this transport */
1669 g_object_get (trans->stream->transport, "certificate", &cert, NULL);
1672 gchar *fingerprint, *val;
1675 _generate_fingerprint_from_certificate (cert, G_CHECKSUM_SHA256);
1678 g_strdup_printf ("%s %s",
1679 _g_checksum_to_webrtc_string (G_CHECKSUM_SHA256), fingerprint);
1680 g_free (fingerprint);
1682 gst_sdp_media_add_attribute (media, "fingerprint", val);
1688 GST_INFO_OBJECT (webrtc, "media %u rejected", i);
1689 gst_sdp_media_free (media);
1690 gst_sdp_media_copy (offer_media, &media);
1691 gst_sdp_media_set_port_info (media, 0, 0);
1693 gst_sdp_message_add_media (ret, media);
1694 gst_sdp_media_free (media);
1696 gst_caps_unref (offer_caps);
1699 /* FIXME: can we add not matched transceivers? */
1701 /* XXX: only true for the initial offerer */
1702 g_object_set (webrtc->priv->ice, "controller", FALSE, NULL);
1709 GstStructure *options;
1710 GstPromise *promise;
1711 GstWebRTCSDPType type;
1715 _create_sdp_task (GstWebRTCBin * webrtc, struct create_sdp *data)
1717 GstWebRTCSessionDescription *desc = NULL;
1718 GstSDPMessage *sdp = NULL;
1719 GstStructure *s = NULL;
1721 GST_INFO_OBJECT (webrtc, "creating %s sdp with options %" GST_PTR_FORMAT,
1722 gst_webrtc_sdp_type_to_string (data->type), data->options);
1724 if (data->type == GST_WEBRTC_SDP_TYPE_OFFER)
1725 sdp = _create_offer_task (webrtc, data->options);
1726 else if (data->type == GST_WEBRTC_SDP_TYPE_ANSWER)
1727 sdp = _create_answer_task (webrtc, data->options);
1729 g_assert_not_reached ();
1734 desc = gst_webrtc_session_description_new (data->type, sdp);
1735 s = gst_structure_new ("application/x-gst-promise",
1736 gst_webrtc_sdp_type_to_string (data->type),
1737 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, desc, NULL);
1742 gst_promise_reply (data->promise, s);
1746 gst_webrtc_session_description_free (desc);
1750 _free_create_sdp_data (struct create_sdp *data)
1753 gst_structure_free (data->options);
1754 gst_promise_unref (data->promise);
1759 gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc,
1760 const GstStructure * options, GstPromise * promise)
1762 struct create_sdp *data = g_new0 (struct create_sdp, 1);
1765 data->options = gst_structure_copy (options);
1766 data->promise = gst_promise_ref (promise);
1767 data->type = GST_WEBRTC_SDP_TYPE_OFFER;
1769 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
1770 data, (GDestroyNotify) _free_create_sdp_data);
1774 gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc,
1775 const GstStructure * options, GstPromise * promise)
1777 struct create_sdp *data = g_new0 (struct create_sdp, 1);
1780 data->options = gst_structure_copy (options);
1781 data->promise = gst_promise_ref (promise);
1782 data->type = GST_WEBRTC_SDP_TYPE_ANSWER;
1784 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task,
1785 data, (GDestroyNotify) _free_create_sdp_data);
1788 static GstWebRTCBinPad *
1789 _create_pad_for_sdp_media (GstWebRTCBin * webrtc, GstPadDirection direction,
1792 GstWebRTCBinPad *pad;
1796 g_strdup_printf ("%s_%u", direction == GST_PAD_SRC ? "src" : "sink",
1798 pad = gst_webrtc_bin_pad_new (pad_name, direction);
1800 pad->mlineindex = media_idx;
1805 static GstWebRTCRTPTransceiver *
1806 _find_transceiver_for_sdp_media (GstWebRTCBin * webrtc,
1807 const GstSDPMessage * sdp, guint media_idx)
1809 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
1810 GstWebRTCRTPTransceiver *ret = NULL;
1813 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
1814 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
1816 if (g_strcmp0 (attr->key, "mid") == 0) {
1818 _find_transceiver (webrtc, attr->value,
1819 (FindTransceiverFunc) match_for_mid)))
1824 ret = _find_transceiver (webrtc, &media_idx,
1825 (FindTransceiverFunc) transceiver_match_for_mline);
1828 GST_TRACE_OBJECT (webrtc, "Found transceiver %" GST_PTR_FORMAT, ret);
1833 _connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
1836 * ,-------------------------webrtcbin-------------------------,
1838 * ; ,-------rtpbin-------, ,--transport_send_%u--, ;
1839 * ; ; send_rtp_src_%u o---o rtp_sink ; ;
1841 * ; ; send_rtcp_src_%u o---o rtcp_sink ; ;
1842 * ; sink_%u ; ; '---------------------' ;
1843 * o----------o send_rtp_sink_%u ; ;
1844 * ; '--------------------' ;
1845 * '--------------------- -------------------------------------'
1847 GstPadTemplate *rtp_templ;
1850 WebRTCTransceiver *trans;
1852 g_return_val_if_fail (pad->trans != NULL, NULL);
1854 GST_INFO_OBJECT (pad, "linking input stream %u", pad->mlineindex);
1857 _find_pad_template (webrtc->rtpbin, GST_PAD_SINK, GST_PAD_REQUEST,
1858 "send_rtp_sink_%u");
1859 g_assert (rtp_templ);
1861 pad_name = g_strdup_printf ("send_rtp_sink_%u", pad->mlineindex);
1863 gst_element_request_pad (webrtc->rtpbin, rtp_templ, pad_name, NULL);
1865 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), rtp_sink);
1866 gst_object_unref (rtp_sink);
1868 trans = WEBRTC_TRANSCEIVER (pad->trans);
1869 if (!trans->stream) {
1870 TransportStream *item;
1872 item = _find_transport_for_session (webrtc, pad->mlineindex);
1874 item = _create_transport_channel (webrtc, pad->mlineindex);
1875 webrtc_transceiver_set_transport (trans, item);
1878 pad_name = g_strdup_printf ("send_rtp_src_%u", pad->mlineindex);
1879 if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
1880 GST_ELEMENT (trans->stream->send_bin), "rtp_sink"))
1881 g_warn_if_reached ();
1884 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->send_bin));
1886 return GST_PAD (pad);
1889 /* output pads are receiving elements */
1890 static GstWebRTCBinPad *
1891 _connect_output_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad)
1894 * ,------------------------webrtcbin------------------------,
1895 * ; ,---------rtpbin---------, ;
1896 * ; ,-transport_receive_%u--, ; ; ;
1897 * ; ; rtp_src o---o recv_rtp_sink_%u ; ;
1899 * ; ; rtcp_src o---o recv_rtcp_sink_%u ; ;
1900 * ; '-----------------------' ; ; ; src_%u
1901 * ; ; recv_rtp_src_%u_%u_%u o--o
1902 * ; '------------------------' ;
1903 * '---------------------------------------------------------'
1906 WebRTCTransceiver *trans;
1908 g_return_val_if_fail (pad->trans != NULL, NULL);
1910 GST_INFO_OBJECT (pad, "linking output stream %u", pad->mlineindex);
1912 trans = WEBRTC_TRANSCEIVER (pad->trans);
1913 if (!trans->stream) {
1914 TransportStream *item;
1916 item = _find_transport_for_session (webrtc, pad->mlineindex);
1918 item = _create_transport_channel (webrtc, pad->mlineindex);
1919 webrtc_transceiver_set_transport (trans, item);
1922 pad_name = g_strdup_printf ("recv_rtp_sink_%u", pad->mlineindex);
1923 if (!gst_element_link_pads (GST_ELEMENT (trans->stream->receive_bin),
1924 "rtp_src", GST_ELEMENT (webrtc->rtpbin), pad_name))
1925 g_warn_if_reached ();
1928 gst_element_sync_state_with_parent (GST_ELEMENT (trans->stream->receive_bin));
1940 _clear_ice_candidate_item (IceCandidateItem ** item)
1942 g_free ((*item)->candidate);
1947 _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item)
1949 GstWebRTCICEStream *stream;
1951 stream = _find_ice_stream_for_session (webrtc, item->mlineindex);
1952 if (stream == NULL) {
1953 GST_WARNING_OBJECT (webrtc, "Unknown mline %u, ignoring", item->mlineindex);
1957 GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s",
1958 item->mlineindex, item->candidate);
1960 gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate);
1964 _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
1965 const GstSDPMessage * sdp, guint media_idx,
1966 GstWebRTCRTPTransceiver * rtp_trans)
1968 WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
1969 TransportStream *stream = trans->stream;
1970 GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction;
1971 GstWebRTCRTPTransceiverDirection new_dir;
1972 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
1973 GstWebRTCDTLSSetup new_setup;
1974 gboolean new_rtcp_mux, new_rtcp_rsize;
1977 rtp_trans->mline = media_idx;
1979 for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
1980 const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
1982 if (g_strcmp0 (attr->key, "mid") == 0) {
1983 g_free (rtp_trans->mid);
1984 rtp_trans->mid = g_strdup (attr->value);
1989 /* FIXME: find an existing transport for e.g. bundle/reconfiguration */
1990 stream = _find_transport_for_session (webrtc, media_idx);
1992 stream = _create_transport_channel (webrtc, media_idx);
1993 webrtc_transceiver_set_transport (trans, stream);
1997 const GstSDPMedia *local_media, *remote_media;
1998 GstWebRTCRTPTransceiverDirection local_dir, remote_dir;
1999 GstWebRTCDTLSSetup local_setup, remote_setup;
2002 GstCaps *global_caps;
2005 gst_sdp_message_get_media (webrtc->current_local_description->sdp,
2008 gst_sdp_message_get_media (webrtc->current_remote_description->sdp,
2011 local_setup = _get_dtls_setup_from_media (local_media);
2012 remote_setup = _get_dtls_setup_from_media (remote_media);
2013 new_setup = _get_final_setup (local_setup, remote_setup);
2014 if (new_setup == GST_WEBRTC_DTLS_SETUP_NONE)
2017 local_dir = _get_direction_from_media (local_media);
2018 remote_dir = _get_direction_from_media (remote_media);
2019 new_dir = _get_final_direction (local_dir, remote_dir);
2020 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE)
2024 proto = gst_sdp_media_get_proto (media);
2025 if (proto != NULL) {
2026 /* Parse global SDP attributes once */
2027 global_caps = gst_caps_new_empty_simple ("application/x-unknown");
2028 GST_DEBUG_OBJECT (webrtc, "mapping sdp session level attributes to caps");
2029 gst_sdp_message_attributes_to_caps (sdp, global_caps);
2030 GST_DEBUG_OBJECT (webrtc, "mapping sdp media level attributes to caps");
2031 gst_sdp_media_attributes_to_caps (media, global_caps);
2033 /* clear the ptmap */
2034 g_array_set_size (stream->ptmap, 0);
2036 len = gst_sdp_media_formats_len (media);
2037 for (i = 0; i < len; i++) {
2038 GstCaps *caps, *outcaps;
2043 pt = atoi (gst_sdp_media_get_format (media, i));
2045 GST_DEBUG_OBJECT (webrtc, " looking at %d pt: %d", i, pt);
2048 caps = gst_sdp_media_get_caps_from_media (media, pt);
2050 GST_WARNING_OBJECT (webrtc, " skipping pt %d without caps", pt);
2054 /* Merge in global caps */
2055 /* Intersect will merge in missing fields to the current caps */
2056 outcaps = gst_caps_intersect (caps, global_caps);
2057 gst_caps_unref (caps);
2059 s = gst_caps_get_structure (outcaps, 0);
2060 gst_structure_set_name (s, "application/x-rtp");
2063 item.caps = outcaps;
2065 g_array_append_val (stream->ptmap, item);
2068 gst_caps_unref (global_caps);
2071 new_rtcp_mux = _media_has_attribute_key (local_media, "rtcp-mux")
2072 && _media_has_attribute_key (remote_media, "rtcp-mux");
2073 new_rtcp_rsize = _media_has_attribute_key (local_media, "rtcp-rsize")
2074 && _media_has_attribute_key (remote_media, "rtcp-rsize");
2078 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
2079 media_idx, &session);
2081 g_object_set (session, "rtcp-reduced-size", new_rtcp_rsize, NULL);
2082 g_object_unref (session);
2087 if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE
2088 && prev_dir != new_dir) {
2089 GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes");
2093 /* FIXME: bundle! */
2094 g_object_set (stream, "rtcp-mux", new_rtcp_mux, NULL);
2096 if (new_dir != prev_dir) {
2097 TransportReceiveBin *receive;
2099 GST_TRACE_OBJECT (webrtc, "transceiver direction change");
2101 /* FIXME: this may not always be true. e.g. bundle */
2102 g_assert (media_idx == stream->session_id);
2104 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY ||
2105 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2106 GstWebRTCBinPad *pad =
2107 _find_pad_for_mline (webrtc, GST_PAD_SINK, media_idx);
2109 GST_DEBUG_OBJECT (webrtc, "found existing send pad %" GST_PTR_FORMAT
2110 " for transceiver %" GST_PTR_FORMAT, pad, trans);
2111 g_assert (pad->trans == rtp_trans);
2112 g_assert (pad->mlineindex == media_idx);
2113 gst_object_unref (pad);
2115 GST_DEBUG_OBJECT (webrtc,
2116 "creating new pad send pad for transceiver %" GST_PTR_FORMAT,
2118 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, media_idx);
2119 pad->trans = gst_object_ref (rtp_trans);
2120 _connect_input_stream (webrtc, pad);
2121 _add_pad (webrtc, pad);
2123 g_object_set (stream, "dtls-client",
2124 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
2126 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
2127 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) {
2128 GstWebRTCBinPad *pad =
2129 _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx);
2131 GST_DEBUG_OBJECT (webrtc, "found existing receive pad %" GST_PTR_FORMAT
2132 " for transceiver %" GST_PTR_FORMAT, pad, trans);
2133 g_assert (pad->trans == rtp_trans);
2134 g_assert (pad->mlineindex == media_idx);
2135 gst_object_unref (pad);
2137 GST_DEBUG_OBJECT (webrtc,
2138 "creating new receive pad for transceiver %" GST_PTR_FORMAT, trans);
2139 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SRC, media_idx);
2140 pad->trans = gst_object_ref (rtp_trans);
2141 _connect_output_stream (webrtc, pad);
2142 /* delay adding the pad until rtpbin creates the recv output pad
2143 * to ghost to so queries/events travel through the pipeline correctly
2144 * as soon as the pad is added */
2145 _add_pad_to_list (webrtc, pad);
2147 g_object_set (stream, "dtls-client",
2148 new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL);
2151 receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin);
2152 if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY ||
2153 new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV)
2154 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS);
2156 transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_DROP);
2158 rtp_trans->mline = media_idx;
2159 rtp_trans->current_direction = new_dir;
2164 _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1,
2169 if (p1->mline != -1)
2176 _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source,
2177 GstWebRTCSessionDescription * sdp)
2181 for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) {
2182 const GstSDPMedia *media = gst_sdp_message_get_media (sdp->sdp, i);
2183 GstWebRTCRTPTransceiver *trans;
2185 /* skip rejected media */
2186 if (gst_sdp_media_get_port (media) == 0)
2189 trans = _find_transceiver_for_sdp_media (webrtc, sdp->sdp, i);
2191 if (source == SDP_LOCAL && sdp->type == GST_WEBRTC_SDP_TYPE_OFFER && !trans) {
2192 GST_ERROR ("State mismatch. Could not find local transceiver by mline.");
2196 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, trans);
2198 trans = _find_transceiver (webrtc, NULL,
2199 (FindTransceiverFunc) _find_compatible_unassociated_transceiver);
2202 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc));
2203 /* XXX: default to the advertised direction in the sdp for new
2204 * transceviers. The spec doesn't actually say what happens here, only
2205 * that calls to setDirection will change the value. Nothing about
2206 * a default value when the transceiver is created internally */
2207 trans->direction = _get_direction_from_media (media);
2208 _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, trans);
2217 _get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx,
2218 gchar ** ufrag, gchar ** pwd)
2226 /* search in the corresponding media section */
2227 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx);
2228 const gchar *tmp_ufrag =
2229 gst_sdp_media_get_attribute_val (media, "ice-ufrag");
2230 const gchar *tmp_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
2231 if (tmp_ufrag && tmp_pwd) {
2232 *ufrag = g_strdup (tmp_ufrag);
2233 *pwd = g_strdup (tmp_pwd);
2238 /* then in the sdp message itself */
2239 for (i = 0; i < gst_sdp_message_attributes_len (sdp); i++) {
2240 const GstSDPAttribute *attr = gst_sdp_message_get_attribute (sdp, i);
2242 if (g_strcmp0 (attr->key, "ice-ufrag") == 0) {
2244 *ufrag = g_strdup (attr->value);
2245 } else if (g_strcmp0 (attr->key, "ice-pwd") == 0) {
2247 *pwd = g_strdup (attr->value);
2250 if (!*ufrag && !*pwd) {
2251 /* Check in the medias themselves. According to JSEP, they should be
2252 * identical FIXME: only for bundle-d streams */
2253 for (i = 0; i < gst_sdp_message_medias_len (sdp); i++) {
2254 const GstSDPMedia *media = gst_sdp_message_get_media (sdp, i);
2255 const gchar *tmp_ufrag =
2256 gst_sdp_media_get_attribute_val (media, "ice-ufrag");
2257 const gchar *tmp_pwd = gst_sdp_media_get_attribute_val (media, "ice-pwd");
2258 if (tmp_ufrag && tmp_pwd) {
2259 *ufrag = g_strdup (tmp_ufrag);
2260 *pwd = g_strdup (tmp_pwd);
2267 struct set_description
2269 GstPromise *promise;
2271 GstWebRTCSessionDescription *sdp;
2274 /* http://w3c.github.io/webrtc-pc/#set-description */
2276 _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd)
2278 GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state;
2279 GError *error = NULL;
2282 gchar *state = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
2283 webrtc->signaling_state);
2285 _enum_value_to_string (GST_TYPE_WEBRTC_SDP_TYPE, sd->sdp->type);
2286 gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp);
2287 GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state",
2288 _sdp_source_to_string (sd->source), type_str, state);
2289 GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text);
2295 if (!validate_sdp (webrtc, sd->source, sd->sdp, &error)) {
2296 GST_ERROR_OBJECT (webrtc, "%s", error->message);
2300 if (webrtc->priv->is_closed) {
2301 GST_WARNING_OBJECT (webrtc, "we are closed");
2305 switch (sd->sdp->type) {
2306 case GST_WEBRTC_SDP_TYPE_OFFER:{
2307 if (sd->source == SDP_LOCAL) {
2308 if (webrtc->pending_local_description)
2309 gst_webrtc_session_description_free
2310 (webrtc->pending_local_description);
2311 webrtc->pending_local_description =
2312 gst_webrtc_session_description_copy (sd->sdp);
2313 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER;
2315 if (webrtc->pending_remote_description)
2316 gst_webrtc_session_description_free
2317 (webrtc->pending_remote_description);
2318 webrtc->pending_remote_description =
2319 gst_webrtc_session_description_copy (sd->sdp);
2320 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER;
2324 case GST_WEBRTC_SDP_TYPE_ANSWER:{
2325 if (sd->source == SDP_LOCAL) {
2326 if (webrtc->current_local_description)
2327 gst_webrtc_session_description_free
2328 (webrtc->current_local_description);
2329 webrtc->current_local_description =
2330 gst_webrtc_session_description_copy (sd->sdp);
2332 if (webrtc->current_remote_description)
2333 gst_webrtc_session_description_free
2334 (webrtc->current_remote_description);
2335 webrtc->current_remote_description = webrtc->pending_remote_description;
2336 webrtc->pending_remote_description = NULL;
2338 if (webrtc->current_remote_description)
2339 gst_webrtc_session_description_free
2340 (webrtc->current_remote_description);
2341 webrtc->current_remote_description =
2342 gst_webrtc_session_description_copy (sd->sdp);
2344 if (webrtc->current_local_description)
2345 gst_webrtc_session_description_free
2346 (webrtc->current_local_description);
2347 webrtc->current_local_description = webrtc->pending_local_description;
2348 webrtc->pending_local_description = NULL;
2351 if (webrtc->pending_local_description)
2352 gst_webrtc_session_description_free (webrtc->pending_local_description);
2353 webrtc->pending_local_description = NULL;
2355 if (webrtc->pending_remote_description)
2356 gst_webrtc_session_description_free
2357 (webrtc->pending_remote_description);
2358 webrtc->pending_remote_description = NULL;
2360 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
2363 case GST_WEBRTC_SDP_TYPE_ROLLBACK:{
2364 GST_FIXME_OBJECT (webrtc, "rollbacks are completely untested");
2365 if (sd->source == SDP_LOCAL) {
2366 if (webrtc->pending_local_description)
2367 gst_webrtc_session_description_free
2368 (webrtc->pending_local_description);
2369 webrtc->pending_local_description = NULL;
2371 if (webrtc->pending_remote_description)
2372 gst_webrtc_session_description_free
2373 (webrtc->pending_remote_description);
2374 webrtc->pending_remote_description = NULL;
2377 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_STABLE;
2380 case GST_WEBRTC_SDP_TYPE_PRANSWER:{
2381 GST_FIXME_OBJECT (webrtc, "pranswers are completely untested");
2382 if (sd->source == SDP_LOCAL) {
2383 if (webrtc->pending_local_description)
2384 gst_webrtc_session_description_free
2385 (webrtc->pending_local_description);
2386 webrtc->pending_local_description =
2387 gst_webrtc_session_description_copy (sd->sdp);
2389 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER;
2391 if (webrtc->pending_remote_description)
2392 gst_webrtc_session_description_free
2393 (webrtc->pending_remote_description);
2394 webrtc->pending_remote_description =
2395 gst_webrtc_session_description_copy (sd->sdp);
2397 new_signaling_state = GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER;
2403 if (new_signaling_state != webrtc->signaling_state) {
2404 gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
2405 webrtc->signaling_state);
2406 gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE,
2407 new_signaling_state);
2408 GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s "
2410 webrtc->signaling_state = new_signaling_state;
2412 g_object_notify (G_OBJECT (webrtc), "signaling-state");
2419 /* TODO: necessary data channel modifications */
2421 if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) {
2423 * If the mid value of an RTCRtpTransceiver was set to a non-null value
2424 * by the RTCSessionDescription that is being rolled back, set the mid
2425 * value of that transceiver to null, as described by [JSEP]
2426 * (section 4.1.7.2.).
2427 * If an RTCRtpTransceiver was created by applying the
2428 * RTCSessionDescription that is being rolled back, and a track has not
2429 * been attached to it via addTrack, remove that transceiver from
2430 * connection's set of transceivers, as described by [JSEP]
2431 * (section 4.1.7.2.).
2432 * Restore the value of connection's [[ sctpTransport]] internal slot
2433 * to its value at the last stable signaling state.
2437 if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) {
2438 gboolean prev_need_negotiation = webrtc->priv->need_negotiation;
2440 /* media modifications */
2441 _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp);
2443 /* If connection's signaling state is now stable, update the
2444 * negotiation-needed flag. If connection's [[ needNegotiation]] slot
2445 * was true both before and after this update, queue a task to check
2446 * connection's [[needNegotiation]] slot and, if still true, fire a
2447 * simple event named negotiationneeded at connection.*/
2448 _update_need_negotiation (webrtc);
2449 if (prev_need_negotiation && webrtc->priv->need_negotiation) {
2450 _check_need_negotiation_task (webrtc, NULL);
2454 if (sd->source == SDP_LOCAL) {
2457 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
2459 TransportStream *item;
2462 item = _find_transport_for_session (webrtc, i);
2464 item = _create_transport_channel (webrtc, i);
2466 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
2467 gst_webrtc_ice_set_local_credentials (webrtc->priv->ice,
2468 item->stream, ufrag, pwd);
2474 if (sd->source == SDP_REMOTE) {
2477 for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) {
2479 TransportStream *item;
2482 item = _find_transport_for_session (webrtc, i);
2484 item = _create_transport_channel (webrtc, i);
2486 _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd);
2487 gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice,
2488 item->stream, ufrag, pwd);
2496 for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) {
2497 IceStreamItem *item =
2498 &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i);
2500 gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream);
2504 if (webrtc->current_local_description && webrtc->current_remote_description) {
2507 for (i = 0; i < webrtc->priv->pending_ice_candidates->len; i++) {
2508 IceCandidateItem *item =
2509 g_array_index (webrtc->priv->pending_ice_candidates,
2510 IceCandidateItem *, i);
2512 _add_ice_candidate (webrtc, item);
2514 g_array_set_size (webrtc->priv->pending_ice_candidates, 0);
2519 gst_promise_reply (sd->promise, NULL);
2524 _free_set_description_data (struct set_description *sd)
2527 gst_promise_unref (sd->promise);
2529 gst_webrtc_session_description_free (sd->sdp);
2534 gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc,
2535 GstWebRTCSessionDescription * remote_sdp, GstPromise * promise)
2537 struct set_description *sd;
2539 if (remote_sdp == NULL)
2541 if (remote_sdp->sdp == NULL)
2544 sd = g_new0 (struct set_description, 1);
2545 if (promise != NULL)
2546 sd->promise = gst_promise_ref (promise);
2547 sd->source = SDP_REMOTE;
2548 sd->sdp = gst_webrtc_session_description_copy (remote_sdp);
2550 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
2551 sd, (GDestroyNotify) _free_set_description_data);
2557 gst_promise_reply (promise, NULL);
2558 g_return_if_reached ();
2563 gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc,
2564 GstWebRTCSessionDescription * local_sdp, GstPromise * promise)
2566 struct set_description *sd;
2568 if (local_sdp == NULL)
2570 if (local_sdp->sdp == NULL)
2573 sd = g_new0 (struct set_description, 1);
2574 if (promise != NULL)
2575 sd->promise = gst_promise_ref (promise);
2576 sd->source = SDP_LOCAL;
2577 sd->sdp = gst_webrtc_session_description_copy (local_sdp);
2579 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task,
2580 sd, (GDestroyNotify) _free_set_description_data);
2586 gst_promise_reply (promise, NULL);
2587 g_return_if_reached ();
2592 _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
2594 if (!webrtc->current_local_description || !webrtc->current_remote_description) {
2595 IceCandidateItem *new = g_new0 (IceCandidateItem, 1);
2596 new->mlineindex = item->mlineindex;
2597 new->candidate = g_strdup (item->candidate);
2599 g_array_append_val (webrtc->priv->pending_ice_candidates, new);
2601 _add_ice_candidate (webrtc, item);
2606 _free_ice_candidate_item (IceCandidateItem * item)
2608 _clear_ice_candidate_item (&item);
2612 gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline,
2615 IceCandidateItem *item;
2617 item = g_new0 (IceCandidateItem, 1);
2618 item->mlineindex = mline;
2619 if (!g_ascii_strncasecmp (attr, "a=candidate:", 12))
2620 item->candidate = g_strdup (attr);
2621 else if (!g_ascii_strncasecmp (attr, "candidate:", 10))
2622 item->candidate = g_strdup_printf ("a=%s", attr);
2623 gst_webrtc_bin_enqueue_task (webrtc,
2624 (GstWebRTCBinFunc) _add_ice_candidate_task, item,
2625 (GDestroyNotify) _free_ice_candidate_item);
2629 _on_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item)
2631 const gchar *cand = item->candidate;
2633 if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) {
2634 /* stripping away "a=" */
2638 GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s",
2639 item->mlineindex, cand);
2642 g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL],
2643 0, item->mlineindex, cand);
2648 _on_ice_candidate (GstWebRTCICE * ice, guint session_id,
2649 gchar * candidate, GstWebRTCBin * webrtc)
2651 IceCandidateItem *item = g_new0 (IceCandidateItem, 1);
2653 /* FIXME: bundle support */
2654 item->mlineindex = session_id;
2655 item->candidate = g_strdup (candidate);
2657 gst_webrtc_bin_enqueue_task (webrtc,
2658 (GstWebRTCBinFunc) _on_ice_candidate_task, item,
2659 (GDestroyNotify) _free_ice_candidate_item);
2662 /* https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm */
2663 static GstStructure *
2664 _get_stats_from_selector (GstWebRTCBin * webrtc, gpointer selector)
2667 GST_FIXME_OBJECT (webrtc, "Implement stats selection");
2669 return gst_structure_copy (webrtc->priv->stats);
2675 GstPromise *promise;
2679 _free_get_stats (struct get_stats *stats)
2682 gst_object_unref (stats->pad);
2684 gst_promise_unref (stats->promise);
2688 /* https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-getstats() */
2690 _get_stats_task (GstWebRTCBin * webrtc, struct get_stats *stats)
2693 gpointer selector = NULL;
2695 gst_webrtc_bin_update_stats (webrtc);
2698 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (stats->pad);
2701 if (GST_PAD_DIRECTION (wpad) == GST_PAD_SRC) {
2702 selector = wpad->trans->receiver;
2704 selector = wpad->trans->sender;
2709 s = _get_stats_from_selector (webrtc, selector);
2710 gst_promise_reply (stats->promise, s);
2714 gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad,
2715 GstPromise * promise)
2717 struct get_stats *stats;
2719 g_return_if_fail (promise != NULL);
2720 g_return_if_fail (pad == NULL || GST_IS_WEBRTC_BIN_PAD (pad));
2722 stats = g_new0 (struct get_stats, 1);
2723 stats->promise = gst_promise_ref (promise);
2724 /* FIXME: check that pad exists in element */
2726 stats->pad = gst_object_ref (pad);
2728 gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task,
2729 stats, (GDestroyNotify) _free_get_stats);
2732 static GstWebRTCRTPTransceiver *
2733 gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
2734 GstWebRTCRTPTransceiverDirection direction, GstCaps * caps)
2736 WebRTCTransceiver *trans;
2737 GstWebRTCRTPTransceiver *rtp_trans;
2739 g_return_val_if_fail (direction != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE,
2742 trans = _create_webrtc_transceiver (webrtc);
2743 rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
2744 rtp_trans->direction = direction;
2746 rtp_trans->codec_preferences = gst_caps_ref (caps);
2748 return gst_object_ref (trans);
2752 _deref_and_unref (GstObject ** object)
2755 gst_object_unref (*object);
2759 gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
2761 GArray *arr = g_array_new (FALSE, TRUE, sizeof (gpointer));
2764 g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref);
2766 for (i = 0; i < webrtc->priv->transceivers->len; i++) {
2767 GstWebRTCRTPTransceiver *trans =
2768 g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *,
2770 gst_object_ref (trans);
2771 g_array_append_val (arr, trans);
2777 /* === rtpbin signal implementations === */
2780 on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad,
2781 GstWebRTCBin * webrtc)
2783 gchar *new_pad_name = NULL;
2785 new_pad_name = gst_pad_get_name (new_pad);
2786 GST_TRACE_OBJECT (webrtc, "new rtpbin pad %s", new_pad_name);
2787 if (g_str_has_prefix (new_pad_name, "recv_rtp_src_")) {
2788 guint32 session_id = 0, ssrc = 0, pt = 0;
2789 GstWebRTCRTPTransceiver *rtp_trans;
2790 WebRTCTransceiver *trans;
2791 TransportStream *stream;
2792 GstWebRTCBinPad *pad;
2794 if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc, &pt)) {
2795 g_critical ("Invalid rtpbin pad name \'%s\'", new_pad_name);
2799 stream = _find_transport_for_session (webrtc, session_id);
2801 g_warn_if_reached ();
2803 /* FIXME: bundle! */
2804 rtp_trans = _find_transceiver_for_mline (webrtc, session_id);
2806 g_warn_if_reached ();
2807 trans = WEBRTC_TRANSCEIVER (rtp_trans);
2808 g_assert (trans->stream == stream);
2810 pad = _find_pad_for_transceiver (webrtc, GST_PAD_SRC, rtp_trans);
2812 GST_TRACE_OBJECT (webrtc, "found pad %" GST_PTR_FORMAT
2813 " for rtpbin pad name %s", pad, new_pad_name);
2815 g_warn_if_reached ();
2816 gst_ghost_pad_set_target (GST_GHOST_PAD (pad), GST_PAD (new_pad));
2818 if (webrtc->priv->running)
2819 gst_pad_set_active (GST_PAD (pad), TRUE);
2820 gst_element_add_pad (GST_ELEMENT (webrtc), GST_PAD (pad));
2821 _remove_pending_pad (webrtc, pad);
2823 gst_object_unref (pad);
2825 g_free (new_pad_name);
2828 /* only used for the receiving streams */
2830 on_rtpbin_request_pt_map (GstElement * rtpbin, guint session_id, guint pt,
2831 GstWebRTCBin * webrtc)
2833 TransportStream *stream;
2836 GST_DEBUG_OBJECT (webrtc, "getting pt map for pt %d in session %d", pt,
2839 stream = _find_transport_for_session (webrtc, session_id);
2841 goto unknown_session;
2843 if ((ret = _transport_stream_get_caps_for_pt (stream, pt)))
2846 GST_TRACE_OBJECT (webrtc, "Found caps %" GST_PTR_FORMAT " for pt %d in "
2847 "session %d", ret, pt, session_id);
2853 GST_DEBUG_OBJECT (webrtc, "unknown session %d", session_id);
2859 on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id,
2860 GstWebRTCBin * webrtc)
2866 on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id,
2867 GstWebRTCBin * webrtc)
2873 on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc,
2874 GstWebRTCBin * webrtc)
2879 on_rtpbin_new_jitterbuffer (GstElement * rtpbin, GstElement * jitterbuffer,
2880 guint session_id, guint ssrc, GstWebRTCBin * webrtc)
2885 _create_rtpbin (GstWebRTCBin * webrtc)
2889 if (!(rtpbin = gst_element_factory_make ("rtpbin", "rtpbin")))
2892 /* mandated by WebRTC */
2893 gst_util_set_object_arg (G_OBJECT (rtpbin), "rtp-profile", "savpf");
2895 g_signal_connect (rtpbin, "pad-added", G_CALLBACK (on_rtpbin_pad_added),
2897 g_signal_connect (rtpbin, "request-pt-map",
2898 G_CALLBACK (on_rtpbin_request_pt_map), webrtc);
2899 g_signal_connect (rtpbin, "request-aux-sender",
2900 G_CALLBACK (on_rtpbin_request_aux_sender), webrtc);
2901 g_signal_connect (rtpbin, "request-aux-receiver",
2902 G_CALLBACK (on_rtpbin_request_aux_receiver), webrtc);
2903 g_signal_connect (rtpbin, "on-ssrc-active",
2904 G_CALLBACK (on_rtpbin_ssrc_active), webrtc);
2905 g_signal_connect (rtpbin, "new-jitterbuffer",
2906 G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc);
2911 static GstStateChangeReturn
2912 gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition)
2914 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
2915 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2917 GST_DEBUG ("changing state: %s => %s",
2918 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
2919 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
2921 switch (transition) {
2922 case GST_STATE_CHANGE_NULL_TO_READY:{
2924 if (!webrtc->rtpbin) {
2925 /* FIXME: is this the right thing for a missing plugin? */
2926 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, (NULL),
2927 ("%s", "rtpbin element is not available"));
2928 return GST_STATE_CHANGE_FAILURE;
2930 nice = gst_element_factory_make ("nicesrc", NULL);
2932 /* FIXME: is this the right thing for a missing plugin? */
2933 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, (NULL),
2934 ("%s", "libnice elements are not available"));
2935 return GST_STATE_CHANGE_FAILURE;
2937 gst_object_unref (nice);
2938 nice = gst_element_factory_make ("nicesink", NULL);
2940 /* FIXME: is this the right thing for a missing plugin? */
2941 GST_ELEMENT_ERROR (webrtc, CORE, MISSING_PLUGIN, (NULL),
2942 ("%s", "libnice elements are not available"));
2943 return GST_STATE_CHANGE_FAILURE;
2945 gst_object_unref (nice);
2946 _update_need_negotiation (webrtc);
2949 case GST_STATE_CHANGE_READY_TO_PAUSED:
2950 webrtc->priv->running = TRUE;
2956 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2957 if (ret == GST_STATE_CHANGE_FAILURE)
2960 switch (transition) {
2961 case GST_STATE_CHANGE_READY_TO_PAUSED:
2962 /* Mangle the return value to NO_PREROLL as that's what really is
2963 * occurring here however cannot be propagated correctly due to nicesrc
2964 * requiring that it be in PLAYING already in order to send/receive
2966 ret = GST_STATE_CHANGE_NO_PREROLL;
2968 case GST_STATE_CHANGE_PAUSED_TO_READY:
2969 webrtc->priv->running = FALSE;
2979 gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
2980 const gchar * name, const GstCaps * caps)
2982 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
2983 GstWebRTCBinPad *pad = NULL;
2984 GstPluginFeature *feature;
2987 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesrc");
2989 gst_object_unref (feature);
2991 GST_ELEMENT_ERROR (element, CORE, MISSING_PLUGIN, NULL,
2992 ("%s", "libnice elements are not available"));
2996 feature = gst_registry_lookup_feature (gst_registry_get (), "nicesink");
2998 gst_object_unref (feature);
3000 GST_ELEMENT_ERROR (element, CORE, MISSING_PLUGIN, NULL,
3001 ("%s", "libnice elements are not available"));
3005 if (templ->direction == GST_PAD_SINK ||
3006 g_strcmp0 (templ->name_template, "sink_%u") == 0) {
3007 GstWebRTCRTPTransceiver *trans;
3009 GST_OBJECT_LOCK (webrtc);
3010 if (name == NULL || strlen (name) < 6 || !g_str_has_prefix (name, "sink_")) {
3011 /* no name given when requesting the pad, use next available int */
3012 serial = webrtc->priv->max_sink_pad_serial++;
3014 /* parse serial number from requested padname */
3015 serial = g_ascii_strtoull (&name[5], NULL, 10);
3016 if (serial > webrtc->priv->max_sink_pad_serial)
3017 webrtc->priv->max_sink_pad_serial = serial;
3019 GST_OBJECT_UNLOCK (webrtc);
3021 pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial);
3022 trans = _find_transceiver_for_mline (webrtc, serial);
3024 GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc)))) {
3025 trans->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV;
3026 trans->mline = serial;
3028 pad->trans = gst_object_ref (trans);
3029 _connect_input_stream (webrtc, pad);
3031 /* TODO: update negotiation-needed */
3032 _add_pad (webrtc, pad);
3035 return GST_PAD (pad);
3039 gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad)
3041 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element);
3042 GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad);
3044 if (webrtc_pad->trans)
3045 gst_object_unref (webrtc_pad->trans);
3046 webrtc_pad->trans = NULL;
3048 _remove_pad (webrtc, webrtc_pad);
3052 gst_webrtc_bin_set_property (GObject * object, guint prop_id,
3053 const GValue * value, GParamSpec * pspec)
3055 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3058 case PROP_STUN_SERVER:
3059 case PROP_TURN_SERVER:
3060 g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
3063 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3069 gst_webrtc_bin_get_property (GObject * object, guint prop_id,
3070 GValue * value, GParamSpec * pspec)
3072 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3076 case PROP_CONNECTION_STATE:
3077 g_value_set_enum (value, webrtc->peer_connection_state);
3079 case PROP_SIGNALING_STATE:
3080 g_value_set_enum (value, webrtc->signaling_state);
3082 case PROP_ICE_GATHERING_STATE:
3083 g_value_set_enum (value, webrtc->ice_gathering_state);
3085 case PROP_ICE_CONNECTION_STATE:
3086 g_value_set_enum (value, webrtc->ice_connection_state);
3088 case PROP_LOCAL_DESCRIPTION:
3089 if (webrtc->pending_local_description)
3090 g_value_set_boxed (value, webrtc->pending_local_description);
3091 else if (webrtc->current_local_description)
3092 g_value_set_boxed (value, webrtc->current_local_description);
3094 g_value_set_boxed (value, NULL);
3096 case PROP_CURRENT_LOCAL_DESCRIPTION:
3097 g_value_set_boxed (value, webrtc->current_local_description);
3099 case PROP_PENDING_LOCAL_DESCRIPTION:
3100 g_value_set_boxed (value, webrtc->pending_local_description);
3102 case PROP_REMOTE_DESCRIPTION:
3103 if (webrtc->pending_remote_description)
3104 g_value_set_boxed (value, webrtc->pending_remote_description);
3105 else if (webrtc->current_remote_description)
3106 g_value_set_boxed (value, webrtc->current_remote_description);
3108 g_value_set_boxed (value, NULL);
3110 case PROP_CURRENT_REMOTE_DESCRIPTION:
3111 g_value_set_boxed (value, webrtc->current_remote_description);
3113 case PROP_PENDING_REMOTE_DESCRIPTION:
3114 g_value_set_boxed (value, webrtc->pending_remote_description);
3116 case PROP_STUN_SERVER:
3117 case PROP_TURN_SERVER:
3118 g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value);
3121 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3128 _free_pending_pad (GstPad * pad)
3130 gst_object_unref (pad);
3134 gst_webrtc_bin_dispose (GObject * object)
3136 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3138 _stop_thread (webrtc);
3140 if (webrtc->priv->ice)
3141 gst_object_unref (webrtc->priv->ice);
3142 webrtc->priv->ice = NULL;
3144 if (webrtc->priv->ice_stream_map)
3145 g_array_free (webrtc->priv->ice_stream_map, TRUE);
3146 webrtc->priv->ice_stream_map = NULL;
3148 G_OBJECT_CLASS (parent_class)->dispose (object);
3152 gst_webrtc_bin_finalize (GObject * object)
3154 GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object);
3156 if (webrtc->priv->transports)
3157 g_array_free (webrtc->priv->transports, TRUE);
3158 webrtc->priv->transports = NULL;
3160 if (webrtc->priv->transceivers)
3161 g_array_free (webrtc->priv->transceivers, TRUE);
3162 webrtc->priv->transceivers = NULL;
3164 if (webrtc->priv->pending_ice_candidates)
3165 g_array_free (webrtc->priv->pending_ice_candidates, TRUE);
3166 webrtc->priv->pending_ice_candidates = NULL;
3168 if (webrtc->priv->session_mid_map)
3169 g_array_free (webrtc->priv->session_mid_map, TRUE);
3170 webrtc->priv->session_mid_map = NULL;
3172 if (webrtc->priv->pending_pads)
3173 g_list_free_full (webrtc->priv->pending_pads,
3174 (GDestroyNotify) _free_pending_pad);
3175 webrtc->priv->pending_pads = NULL;
3177 if (webrtc->current_local_description)
3178 gst_webrtc_session_description_free (webrtc->current_local_description);
3179 webrtc->current_local_description = NULL;
3180 if (webrtc->pending_local_description)
3181 gst_webrtc_session_description_free (webrtc->pending_local_description);
3182 webrtc->pending_local_description = NULL;
3184 if (webrtc->current_remote_description)
3185 gst_webrtc_session_description_free (webrtc->current_remote_description);
3186 webrtc->current_remote_description = NULL;
3187 if (webrtc->pending_remote_description)
3188 gst_webrtc_session_description_free (webrtc->pending_remote_description);
3189 webrtc->pending_remote_description = NULL;
3191 if (webrtc->priv->stats)
3192 gst_structure_free (webrtc->priv->stats);
3193 webrtc->priv->stats = NULL;
3195 G_OBJECT_CLASS (parent_class)->finalize (object);
3199 gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
3201 GObjectClass *gobject_class = (GObjectClass *) klass;
3202 GstElementClass *element_class = (GstElementClass *) klass;
3204 g_type_class_add_private (klass, sizeof (GstWebRTCBinPrivate));
3206 element_class->request_new_pad = gst_webrtc_bin_request_new_pad;
3207 element_class->release_pad = gst_webrtc_bin_release_pad;
3208 element_class->change_state = gst_webrtc_bin_change_state;
3210 gst_element_class_add_static_pad_template (element_class, &sink_template);
3211 gst_element_class_add_static_pad_template (element_class, &src_template);
3213 gst_element_class_set_metadata (element_class, "WebRTC Bin",
3214 "Filter/Network/WebRTC", "A bin for webrtc connections",
3215 "Matthew Waters <matthew@centricular.com>");
3217 gobject_class->get_property = gst_webrtc_bin_get_property;
3218 gobject_class->set_property = gst_webrtc_bin_set_property;
3219 gobject_class->dispose = gst_webrtc_bin_dispose;
3220 gobject_class->finalize = gst_webrtc_bin_finalize;
3222 g_object_class_install_property (gobject_class,
3223 PROP_LOCAL_DESCRIPTION,
3224 g_param_spec_boxed ("local-description", "Local Description",
3225 "The local SDP description to use for this connection",
3226 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
3227 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3229 g_object_class_install_property (gobject_class,
3230 PROP_REMOTE_DESCRIPTION,
3231 g_param_spec_boxed ("remote-description", "Remote Description",
3232 "The remote SDP description to use for this connection",
3233 GST_TYPE_WEBRTC_SESSION_DESCRIPTION,
3234 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3236 g_object_class_install_property (gobject_class,
3238 g_param_spec_string ("stun-server", "STUN Server",
3239 "The STUN server of the form stun://hostname:port",
3240 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3242 g_object_class_install_property (gobject_class,
3244 g_param_spec_string ("turn-server", "TURN Server",
3245 "The TURN server of the form turn(s)://username:password@host:port",
3246 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3248 g_object_class_install_property (gobject_class,
3249 PROP_CONNECTION_STATE,
3250 g_param_spec_enum ("connection-state", "Connection State",
3251 "The overall connection state of this element",
3252 GST_TYPE_WEBRTC_PEER_CONNECTION_STATE,
3253 GST_WEBRTC_PEER_CONNECTION_STATE_NEW,
3254 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3256 g_object_class_install_property (gobject_class,
3257 PROP_SIGNALING_STATE,
3258 g_param_spec_enum ("signaling-state", "Signaling State",
3259 "The signaling state of this element",
3260 GST_TYPE_WEBRTC_SIGNALING_STATE,
3261 GST_WEBRTC_SIGNALING_STATE_STABLE,
3262 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3264 g_object_class_install_property (gobject_class,
3265 PROP_ICE_CONNECTION_STATE,
3266 g_param_spec_enum ("ice-connection-state", "ICE connection state",
3267 "The collective connection state of all ICETransport's",
3268 GST_TYPE_WEBRTC_ICE_CONNECTION_STATE,
3269 GST_WEBRTC_ICE_CONNECTION_STATE_NEW,
3270 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3272 g_object_class_install_property (gobject_class,
3273 PROP_ICE_GATHERING_STATE,
3274 g_param_spec_enum ("ice-gathering-state", "ICE gathering state",
3275 "The collective gathering state of all ICETransport's",
3276 GST_TYPE_WEBRTC_ICE_GATHERING_STATE,
3277 GST_WEBRTC_ICE_GATHERING_STATE_NEW,
3278 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3281 * GstWebRTCBin::create-offer:
3282 * @object: the #GstWebRtcBin
3283 * @options: create-offer options
3284 * @promise: a #GstPromise which will contain the offer
3286 gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] =
3287 g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass),
3288 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3289 G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL,
3290 g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
3294 * GstWebRTCBin::create-answer:
3295 * @object: the #GstWebRtcBin
3296 * @options: create-answer options
3297 * @promise: a #GstPromise which will contain the answer
3299 gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] =
3300 g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass),
3301 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3302 G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL,
3303 g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE,
3307 * GstWebRTCBin::set-local-description:
3308 * @object: the #GstWebRtcBin
3309 * @type: the type of description being set
3310 * @sdp: a #GstSDPMessage description
3311 * @promise (allow-none): a #GstPromise to be notified when it's set
3313 gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] =
3314 g_signal_new_class_handler ("set-local-description",
3315 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3316 G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL,
3317 g_cclosure_marshal_generic, G_TYPE_NONE, 2,
3318 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
3321 * GstWebRTCBin::set-remote-description:
3322 * @object: the #GstWebRtcBin
3323 * @type: the type of description being set
3324 * @sdp: a #GstSDPMessage description
3325 * @promise (allow-none): a #GstPromise to be notified when it's set
3327 gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] =
3328 g_signal_new_class_handler ("set-remote-description",
3329 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3330 G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL,
3331 g_cclosure_marshal_generic, G_TYPE_NONE, 2,
3332 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE);
3335 * GstWebRTCBin::add-ice-candidate:
3336 * @object: the #GstWebRtcBin
3337 * @ice-candidate: an ice candidate
3339 gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] =
3340 g_signal_new_class_handler ("add-ice-candidate",
3341 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3342 G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL,
3343 g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
3346 * GstWebRTCBin::get-stats:
3347 * @object: the #GstWebRtcBin
3348 * @promise: a #GstPromise for the result
3350 * The @promise will contain the result of retrieving the session statistics.
3351 * The structure will be named 'application/x-webrtc-stats and contain the
3352 * following based on the webrtc-stats spec available from
3353 * https://www.w3.org/TR/webrtc-stats/. As the webrtc-stats spec is a draft
3354 * and is constantly changing these statistics may be changed to fit with
3357 * Each field key is a unique identifer for each RTCStats
3358 * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another
3359 * GstStructure) in the RTCStatsReport
3360 * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported
3361 * field in the RTCStats subclass is outlined below.
3363 * Each statistics structure contains the following values as defined by
3364 * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
3366 * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
3367 * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
3368 * "id" G_TYPE_STRING unique identifier
3370 * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
3372 * "payload-type" G_TYPE_UINT the rtp payload number in use
3373 * "clock-rate" G_TYPE_UINT the rtp clock-rate
3375 * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
3377 * "ssrc" G_TYPE_STRING the rtp sequence src in use
3378 * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
3379 * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
3380 * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
3381 * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
3382 * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
3384 * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
3386 * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
3387 * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
3388 * "packets-lost" G_TYPE_UINT number of packets lost
3389 * "jitter" G_TYPE_DOUBLE packet jitter measured in secondss
3391 * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
3393 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPSTreamStats
3395 * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
3397 * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
3398 * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
3400 * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
3402 * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
3403 * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
3405 * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
3407 * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
3409 * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
3411 * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
3414 gst_webrtc_bin_signals[GET_STATS_SIGNAL] =
3415 g_signal_new_class_handler ("get-stats",
3416 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3417 G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL,
3418 g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_PAD,
3422 * GstWebRTCBin::on-negotiation-needed:
3423 * @object: the #GstWebRtcBin
3425 gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] =
3426 g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass),
3427 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
3431 * GstWebRTCBin::on-ice-candidate:
3432 * @object: the #GstWebRtcBin
3433 * @candidate: the ICE candidate
3435 gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] =
3436 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
3437 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
3438 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
3441 * GstWebRTCBin::add-transceiver:
3442 * @object: the #GstWebRtcBin
3443 * @direction: the direction of the new transceiver
3444 * @caps: (allow none): the codec preferences for this transceiver
3446 * Returns: the new #GstWebRTCRTPTransceiver
3448 gst_webrtc_bin_signals[ADD_TRANSCEIVER_SIGNAL] =
3449 g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass),
3450 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3451 G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL,
3452 g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2,
3453 GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS);
3456 * GstWebRTCBin::get-transceivers:
3457 * @object: the #GstWebRtcBin
3459 * Returns: a #GArray of #GstWebRTCRTPTransceivers
3461 gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] =
3462 g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass),
3463 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3464 G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL,
3465 g_cclosure_marshal_generic, G_TYPE_ARRAY, 0);
3469 _deref_unparent_and_unref (GObject ** object)
3471 GstObject *obj = GST_OBJECT (*object);
3473 GST_OBJECT_PARENT (obj) = NULL;
3475 gst_object_unref (*object);
3479 _transport_free (GObject ** object)
3481 TransportStream *stream = (TransportStream *) * object;
3482 GstWebRTCBin *webrtc;
3484 webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream));
3486 if (stream->transport) {
3487 g_signal_handlers_disconnect_by_data (stream->transport->transport, webrtc);
3488 g_signal_handlers_disconnect_by_data (stream->transport, webrtc);
3490 if (stream->rtcp_transport) {
3491 g_signal_handlers_disconnect_by_data (stream->rtcp_transport->transport,
3493 g_signal_handlers_disconnect_by_data (stream->rtcp_transport, webrtc);
3496 gst_object_unref (*object);
3500 gst_webrtc_bin_init (GstWebRTCBin * webrtc)
3503 G_TYPE_INSTANCE_GET_PRIVATE ((webrtc), GST_TYPE_WEBRTC_BIN,
3504 GstWebRTCBinPrivate);
3506 _start_thread (webrtc);
3508 webrtc->rtpbin = _create_rtpbin (webrtc);
3509 gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin);
3511 webrtc->priv->transceivers = g_array_new (FALSE, TRUE, sizeof (gpointer));
3512 g_array_set_clear_func (webrtc->priv->transceivers,
3513 (GDestroyNotify) _deref_unparent_and_unref);
3515 webrtc->priv->transports = g_array_new (FALSE, TRUE, sizeof (gpointer));
3516 g_array_set_clear_func (webrtc->priv->transports,
3517 (GDestroyNotify) _transport_free);
3519 webrtc->priv->session_mid_map =
3520 g_array_new (FALSE, TRUE, sizeof (SessionMidItem));
3521 g_array_set_clear_func (webrtc->priv->session_mid_map,
3522 (GDestroyNotify) clear_session_mid_item);
3524 webrtc->priv->ice = gst_webrtc_ice_new ();
3525 g_signal_connect (webrtc->priv->ice, "on-ice-candidate",
3526 G_CALLBACK (_on_ice_candidate), webrtc);
3527 webrtc->priv->ice_stream_map =
3528 g_array_new (FALSE, TRUE, sizeof (IceStreamItem));
3529 webrtc->priv->pending_ice_candidates =
3530 g_array_new (FALSE, TRUE, sizeof (IceCandidateItem *));
3531 g_array_set_clear_func (webrtc->priv->pending_ice_candidates,
3532 (GDestroyNotify) _clear_ice_candidate_item);