From 5e35b40cd099a027f6d56b2da9017678e205c0da Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Thu, 31 Dec 2020 20:31:09 +0900 Subject: [PATCH] webrtc: Update codes based on 1.18.4 files below are updated. - ext/webrtc/* - gst-libs/gst/webrtc/* Change-Id: Ic2622a1e0275a4e11ccd806a631d923a5601308e Signed-off-by: Sangchul Lee --- ext/webrtc/gstwebrtcbin.c | 2662 ++++++++++++++----- ext/webrtc/gstwebrtcbin.h | 25 +- ext/webrtc/gstwebrtcice.c | 400 ++- ext/webrtc/gstwebrtcice.h | 21 +- ext/webrtc/gstwebrtcstats.c | 308 ++- ext/webrtc/meson.build | 1 + ext/webrtc/nicetransport.c | 4 +- ext/webrtc/sctptransport.c | 6 +- ext/webrtc/sctptransport.h | 1 + ext/webrtc/transportreceivebin.c | 147 +- ext/webrtc/transportreceivebin.h | 4 +- ext/webrtc/transportsendbin.c | 115 +- ext/webrtc/transportstream.c | 38 + ext/webrtc/transportstream.h | 8 + ext/webrtc/utils.c | 73 +- ext/webrtc/utils.h | 6 + ext/webrtc/webrtcdatachannel.c | 702 ++--- ext/webrtc/webrtcdatachannel.h | 53 +- ext/webrtc/webrtcsdp.c | 111 +- ext/webrtc/webrtcsdp.h | 15 + ext/webrtc/webrtctransceiver.c | 11 +- ext/webrtc/webrtctransceiver.h | 2 + gst-libs/gst/webrtc/Makefile.am | 6 +- gst-libs/gst/webrtc/datachannel.c | 559 ++++ gst-libs/gst/webrtc/datachannel.h | 115 + gst-libs/gst/webrtc/dtlstransport.c | 38 +- gst-libs/gst/webrtc/dtlstransport.h | 5 +- gst-libs/gst/webrtc/icetransport.c | 10 +- gst-libs/gst/webrtc/icetransport.h | 5 +- gst-libs/gst/webrtc/meson.build | 2 + gst-libs/gst/webrtc/rtcsessiondescription.c | 2 +- gst-libs/gst/webrtc/rtcsessiondescription.h | 4 +- gst-libs/gst/webrtc/rtpreceiver.c | 2 +- gst-libs/gst/webrtc/rtpreceiver.h | 5 +- gst-libs/gst/webrtc/rtpsender.c | 2 +- gst-libs/gst/webrtc/rtpsender.h | 5 +- gst-libs/gst/webrtc/rtptransceiver.c | 30 +- gst-libs/gst/webrtc/rtptransceiver.h | 6 +- gst-libs/gst/webrtc/webrtc.h | 1 + gst-libs/gst/webrtc/webrtc_fwd.h | 142 +- packaging/gst-plugins-bad.spec | 2 + 41 files changed, 3909 insertions(+), 1745 deletions(-) create mode 100644 gst-libs/gst/webrtc/datachannel.c create mode 100644 gst-libs/gst/webrtc/datachannel.h diff --git a/ext/webrtc/gstwebrtcbin.c b/ext/webrtc/gstwebrtcbin.c index eddf6ef1a..c261c8be0 100644 --- a/ext/webrtc/gstwebrtcbin.c +++ b/ext/webrtc/gstwebrtcbin.c @@ -49,6 +49,14 @@ #define PC_COND_BROADCAST(w) (g_cond_broadcast(PC_GET_COND(w))) #define PC_COND_SIGNAL(w) (g_cond_signal(PC_GET_COND(w))) +#define ICE_GET_LOCK(w) (&w->priv->ice_lock) +#define ICE_LOCK(w) (g_mutex_lock (ICE_GET_LOCK(w))) +#define ICE_UNLOCK(w) (g_mutex_unlock (ICE_GET_LOCK(w))) + + +/* The extra time for the rtpstorage compared to the RTP jitterbuffer (in ms) */ +#define RTPSTORAGE_EXTRA_TIME (50) + /* * This webrtcbin implements the majority of the W3's peerconnection API and * implementation guide where possible. Generating offers, answers and setting @@ -87,7 +95,7 @@ * balanced bundle policy * setting custom DTLS certificates * - * seperate session id's from mlineindex properly + * separate session id's from mlineindex properly * how to deal with replacing a input/output track/stream */ @@ -96,6 +104,21 @@ static void _update_need_negotiation (GstWebRTCBin * webrtc); #define GST_CAT_DEFAULT gst_webrtc_bin_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS ("application/x-rtp")); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("application/x-rtp")); + +enum +{ + PROP_PAD_TRANSCEIVER = 1, +}; + static gboolean _have_nice_elements (GstWebRTCBin * webrtc) { @@ -191,7 +214,12 @@ static void gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { + GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (object); + switch (prop_id) { + case PROP_PAD_TRANSCEIVER: + g_value_set_object (value, pad->trans); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -222,24 +250,41 @@ gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass) gobject_class->get_property = gst_webrtc_bin_pad_get_property; gobject_class->set_property = gst_webrtc_bin_pad_set_property; gobject_class->finalize = gst_webrtc_bin_pad_finalize; + + g_object_class_install_property (gobject_class, + PROP_PAD_TRANSCEIVER, + g_param_spec_object ("transceiver", "Transceiver", + "Transceiver associated with this pad", + GST_TYPE_WEBRTC_RTP_TRANSCEIVER, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } static gboolean gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad); + GstWebRTCBin *webrtc = GST_WEBRTC_BIN (parent); + gboolean check_negotiation = FALSE; if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) { GstCaps *caps; - gboolean do_update; gst_event_parse_caps (event, &caps); - do_update = (!wpad->received_caps + check_negotiation = (!wpad->received_caps || gst_caps_is_equal (wpad->received_caps, caps)); gst_caps_replace (&wpad->received_caps, caps); - if (do_update) - _update_need_negotiation (GST_WEBRTC_BIN (parent)); + GST_DEBUG_OBJECT (parent, + "On %" GST_PTR_FORMAT " checking negotiation? %u, caps %" + GST_PTR_FORMAT, pad, check_negotiation, caps); + } else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { + check_negotiation = TRUE; + } + + if (check_negotiation) { + PC_LOCK (webrtc); + _update_need_negotiation (webrtc); + PC_UNLOCK (webrtc); } return gst_pad_event_default (pad, parent, event); @@ -253,17 +298,23 @@ gst_webrtc_bin_pad_init (GstWebRTCBinPad * pad) static GstWebRTCBinPad * gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction) { - GstWebRTCBinPad *pad = + GstWebRTCBinPad *pad; + GstPadTemplate *template; + + if (direction == GST_PAD_SINK) + template = gst_static_pad_template_get (&sink_template); + else if (direction == GST_PAD_SRC) + template = gst_static_pad_template_get (&src_template); + else + g_assert_not_reached (); + + pad = g_object_new (gst_webrtc_bin_pad_get_type (), "name", name, "direction", - direction, NULL); + direction, "template", template, NULL); + gst_object_unref (template); gst_pad_set_event_function (GST_PAD (pad), gst_webrtcbin_sink_event); - if (!gst_ghost_pad_construct (GST_GHOST_PAD (pad))) { - gst_object_unref (pad); - return NULL; - } - GST_DEBUG_OBJECT (pad, "new visible pad with direction %s", direction == GST_PAD_SRC ? "src" : "sink"); return pad; @@ -273,22 +324,11 @@ gst_webrtc_bin_pad_new (const gchar * name, GstPadDirection direction) G_DEFINE_TYPE_WITH_CODE (GstWebRTCBin, gst_webrtc_bin, GST_TYPE_BIN, G_ADD_PRIVATE (GstWebRTCBin) GST_DEBUG_CATEGORY_INIT (gst_webrtc_bin_debug, "webrtcbin", 0, - "webrtcbin element"); - ); + "webrtcbin element");); static GstPad *_connect_input_stream (GstWebRTCBin * webrtc, GstWebRTCBinPad * pad); -static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS ("application/x-rtp")); - -static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src_%u", - GST_PAD_SRC, - GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("application/x-rtp")); - enum { SIGNAL_0, @@ -327,6 +367,8 @@ enum PROP_TURN_SERVER, PROP_BUNDLE_POLICY, PROP_ICE_TRANSPORT_POLICY, + PROP_ICE_AGENT, + PROP_LATENCY }; static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 }; @@ -393,8 +435,7 @@ _find_transceiver (GstWebRTCBin * webrtc, gconstpointer data, for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *transceiver = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + g_ptr_array_index (webrtc->priv->transceivers, i); if (func (transceiver, data)) return transceiver; @@ -440,9 +481,7 @@ _find_transport (GstWebRTCBin * webrtc, gconstpointer data, int i; for (i = 0; i < webrtc->priv->transports->len; i++) { - TransportStream *stream = - g_array_index (webrtc->priv->transports, TransportStream *, - i); + TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i); if (func (stream, data)) return stream; @@ -506,19 +545,18 @@ _find_pad (GstWebRTCBin * webrtc, gconstpointer data, FindPadFunc func) return NULL; } -typedef gboolean (*FindDataChannelFunc) (GstWebRTCDataChannel * p1, +typedef gboolean (*FindDataChannelFunc) (WebRTCDataChannel * p1, gconstpointer data); -static GstWebRTCDataChannel * +static WebRTCDataChannel * _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data, FindDataChannelFunc func) { int i; for (i = 0; i < webrtc->priv->data_channels->len; i++) { - GstWebRTCDataChannel *channel = - g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, - i); + WebRTCDataChannel *channel = + g_ptr_array_index (webrtc->priv->data_channels, i); if (func (channel, data)) return channel; @@ -528,15 +566,15 @@ _find_data_channel (GstWebRTCBin * webrtc, gconstpointer data, } static gboolean -data_channel_match_for_id (GstWebRTCDataChannel * channel, gint * id) +data_channel_match_for_id (WebRTCDataChannel * channel, gint * id) { - return channel->id == *id; + return channel->parent.id == *id; } -static GstWebRTCDataChannel * +static WebRTCDataChannel * _find_data_channel_for_id (GstWebRTCBin * webrtc, gint id) { - GstWebRTCDataChannel *channel; + WebRTCDataChannel *channel; channel = _find_data_channel (webrtc, &id, (FindDataChannelFunc) data_channel_match_for_id); @@ -661,9 +699,12 @@ _gst_pc_thread (GstWebRTCBin * webrtc) * tasks */ g_main_loop_run (webrtc->priv->loop); - PC_LOCK (webrtc); + GST_OBJECT_LOCK (webrtc); g_main_context_unref (webrtc->priv->main_context); webrtc->priv->main_context = NULL; + GST_OBJECT_UNLOCK (webrtc); + + PC_LOCK (webrtc); g_main_loop_unref (webrtc->priv->loop); webrtc->priv->loop = NULL; PC_COND_BROADCAST (webrtc); @@ -675,9 +716,13 @@ _gst_pc_thread (GstWebRTCBin * webrtc) static void _start_thread (GstWebRTCBin * webrtc) { + gchar *name; + PC_LOCK (webrtc); - webrtc->priv->thread = g_thread_new ("gst-pc-ops", - (GThreadFunc) _gst_pc_thread, webrtc); + name = g_strdup_printf ("%s:pc", GST_OBJECT_NAME (webrtc)); + webrtc->priv->thread = g_thread_new (name, (GThreadFunc) _gst_pc_thread, + webrtc); + g_free (name); while (!webrtc->priv->loop) PC_COND_WAIT (webrtc); @@ -688,8 +733,11 @@ _start_thread (GstWebRTCBin * webrtc) static void _stop_thread (GstWebRTCBin * webrtc) { - PC_LOCK (webrtc); + GST_OBJECT_LOCK (webrtc); webrtc->priv->is_closed = TRUE; + GST_OBJECT_UNLOCK (webrtc); + + PC_LOCK (webrtc); g_main_loop_quit (webrtc->priv->loop); while (webrtc->priv->loop) PC_COND_WAIT (webrtc); @@ -703,6 +751,18 @@ _execute_op (GstWebRTCBinTask * op) { PC_LOCK (op->webrtc); if (op->webrtc->priv->is_closed) { + if (op->promise) { + GError *error = + g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED, + "webrtcbin is closed. aborting execution."); + GstStructure *s = + gst_structure_new ("application/x-gstwebrtcbin-promise-error", + "error", G_TYPE_ERROR, error, NULL); + + gst_promise_reply (op->promise, s); + + g_clear_error (&error); + } GST_DEBUG_OBJECT (op->webrtc, "Peerconnection is closed, aborting execution"); goto out; @@ -720,36 +780,55 @@ _free_op (GstWebRTCBinTask * op) { if (op->notify) op->notify (op->data); + if (op->promise) + gst_promise_unref (op->promise); g_free (op); } -void +/* + * @promise is for correctly signalling the failure case to the caller when + * the user supplies it. Without passing it in, the promise would never + * be replied to in the case that @webrtc becomes closed between the idle + * source addition and the the execution of the idle source. + */ +gboolean gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func, - gpointer data, GDestroyNotify notify) + gpointer data, GDestroyNotify notify, GstPromise * promise) { GstWebRTCBinTask *op; + GMainContext *ctx; GSource *source; - g_return_if_fail (GST_IS_WEBRTC_BIN (webrtc)); + g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE); + GST_OBJECT_LOCK (webrtc); if (webrtc->priv->is_closed) { + GST_OBJECT_UNLOCK (webrtc); GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution"); if (notify) notify (data); - return; + return FALSE; } + ctx = g_main_context_ref (webrtc->priv->main_context); + GST_OBJECT_UNLOCK (webrtc); + op = g_new0 (GstWebRTCBinTask, 1); op->webrtc = webrtc; op->op = func; op->data = data; op->notify = notify; + if (promise) + op->promise = gst_promise_ref (promise); source = g_idle_source_new (); g_source_set_priority (source, G_PRIORITY_DEFAULT); g_source_set_callback (source, (GSourceFunc) _execute_op, op, (GDestroyNotify) _free_op); - g_source_attach (source, webrtc->priv->main_context); + g_source_attach (source, ctx); g_source_unref (source); + g_main_context_unref (ctx); + + return TRUE; } /* https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate */ @@ -758,23 +837,29 @@ _collate_ice_connection_states (GstWebRTCBin * webrtc) { #define STATE(val) GST_WEBRTC_ICE_CONNECTION_STATE_ ## val GstWebRTCICEConnectionState any_state = 0; - gboolean all_closed = TRUE; + gboolean all_new_or_closed = TRUE; + gboolean all_completed_or_closed = TRUE; + gboolean all_connected_completed_or_closed = TRUE; int i; for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *rtp_trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + g_ptr_array_index (webrtc->priv->transceivers, i); WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans); TransportStream *stream = trans->stream; GstWebRTCICETransport *transport, *rtcp_transport; GstWebRTCICEConnectionState ice_state; gboolean rtcp_mux = FALSE; - if (rtp_trans->stopped) + if (rtp_trans->stopped) { + GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans); continue; - if (!rtp_trans->mid) + } + + if (!rtp_trans->mid) { + GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans); continue; + } g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL); @@ -782,18 +867,34 @@ _collate_ice_connection_states (GstWebRTCBin * webrtc) /* get transport state */ g_object_get (transport, "state", &ice_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p state 0x%x", rtp_trans, + ice_state); any_state |= (1 << ice_state); - if (ice_state != STATE (CLOSED)) - all_closed = FALSE; + + if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED)) + all_new_or_closed = FALSE; + if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED)) + all_completed_or_closed = FALSE; + if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED) + && ice_state != STATE (CLOSED)) + all_connected_completed_or_closed = FALSE; rtcp_transport = webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport; if (!rtcp_mux && rtcp_transport && transport != rtcp_transport) { g_object_get (rtcp_transport, "state", &ice_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p RTCP state 0x%x", rtp_trans, + ice_state); any_state |= (1 << ice_state); - if (ice_state != STATE (CLOSED)) - all_closed = FALSE; + + if (ice_state != STATE (NEW) && ice_state != STATE (CLOSED)) + all_new_or_closed = FALSE; + if (ice_state != STATE (COMPLETED) && ice_state != STATE (CLOSED)) + all_completed_or_closed = FALSE; + if (ice_state != STATE (CONNECTED) && ice_state != STATE (COMPLETED) + && ice_state != STATE (CLOSED)) + all_connected_completed_or_closed = FALSE; } } @@ -803,47 +904,40 @@ _collate_ice_connection_states (GstWebRTCBin * webrtc) GST_TRACE_OBJECT (webrtc, "returning closed"); return STATE (CLOSED); } - /* Any of the RTCIceTransport s are in the failed state. */ + /* Any of the RTCIceTransports are in the failed state. */ if (any_state & (1 << STATE (FAILED))) { GST_TRACE_OBJECT (webrtc, "returning failed"); return STATE (FAILED); } - /* Any of the RTCIceTransport s are in the disconnected state and - * none of them are in the failed state. */ + /* Any of the RTCIceTransports are in the disconnected state. */ if (any_state & (1 << STATE (DISCONNECTED))) { GST_TRACE_OBJECT (webrtc, "returning disconnected"); return STATE (DISCONNECTED); } - /* Any of the RTCIceTransport's are in the checking state and none of them - * are in the failed or disconnected state. */ - if (any_state & (1 << STATE (CHECKING))) { - GST_TRACE_OBJECT (webrtc, "returning checking"); - return STATE (CHECKING); - } - /* Any of the RTCIceTransport s are in the new state and none of them are - * in the checking, failed or disconnected state, or all RTCIceTransport's - * are in the closed state. */ - if ((any_state & (1 << STATE (NEW))) || all_closed) { + /* All of the RTCIceTransports are in the new or closed state, or there are + * no transports. */ + if (all_new_or_closed || webrtc->priv->transceivers->len == 0) { GST_TRACE_OBJECT (webrtc, "returning new"); return STATE (NEW); } - /* All RTCIceTransport s are in the connected, completed or closed state - * and at least one of them is in the connected state. */ - if (any_state & (1 << STATE (CONNECTED) | 1 << STATE (COMPLETED) | 1 << - STATE (CLOSED)) && any_state & (1 << STATE (CONNECTED))) { - GST_TRACE_OBJECT (webrtc, "returning connected"); - return STATE (CONNECTED); + /* Any of the RTCIceTransports are in the checking or new state. */ + if ((any_state & (1 << STATE (CHECKING))) || (any_state & (1 << STATE (NEW)))) { + GST_TRACE_OBJECT (webrtc, "returning checking"); + return STATE (CHECKING); } - /* All RTCIceTransport s are in the completed or closed state and at least - * one of them is in the completed state. */ - if (any_state & (1 << STATE (COMPLETED) | 1 << STATE (CLOSED)) - && any_state & (1 << STATE (COMPLETED))) { + /* All RTCIceTransports are in the completed or closed state. */ + if (all_completed_or_closed) { + GST_TRACE_OBJECT (webrtc, "returning completed"); + return STATE (COMPLETED); + } + /* All RTCIceTransports are in the connected, completed or closed state. */ + if (all_connected_completed_or_closed) { GST_TRACE_OBJECT (webrtc, "returning connected"); return STATE (CONNECTED); } - GST_FIXME ("unspecified situation, returning new"); - return STATE (NEW); + GST_FIXME ("unspecified situation, returning old state"); + return webrtc->ice_connection_state; #undef STATE } @@ -858,34 +952,55 @@ _collate_ice_gathering_states (GstWebRTCBin * webrtc) for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *rtp_trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + g_ptr_array_index (webrtc->priv->transceivers, i); WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans); TransportStream *stream = trans->stream; + GstWebRTCDTLSTransport *dtls_transport; GstWebRTCICETransport *transport, *rtcp_transport; GstWebRTCICEGatheringState ice_state; gboolean rtcp_mux = FALSE; - if (rtp_trans->stopped) - continue; - if (!rtp_trans->mid) + if (rtp_trans->stopped || stream == NULL) { + GST_TRACE_OBJECT (webrtc, "transceiver %p stopped or unassociated", + rtp_trans); continue; + } + + /* We only have a mid in the transceiver after we got the SDP answer, + * which is usually long after gathering has finished */ + if (!rtp_trans->mid) { + GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans); + } g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL); - transport = webrtc_transceiver_get_dtls_transport (rtp_trans)->transport; + dtls_transport = webrtc_transceiver_get_dtls_transport (rtp_trans); + if (dtls_transport == NULL) { + GST_WARNING ("Transceiver %p has no DTLS transport", rtp_trans); + continue; + } + + transport = dtls_transport->transport; /* get gathering state */ g_object_get (transport, "gathering-state", &ice_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p gathering state: 0x%x", rtp_trans, + ice_state); any_state |= (1 << ice_state); if (ice_state != STATE (COMPLETE)) all_completed = FALSE; - rtcp_transport = - webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans)->transport; + dtls_transport = webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans); + if (dtls_transport == NULL) { + GST_WARNING ("Transceiver %p has no DTLS RTCP transport", rtp_trans); + continue; + } + rtcp_transport = dtls_transport->transport; if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) { g_object_get (rtcp_transport, "gathering-state", &ice_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p RTCP gathering state: 0x%x", + rtp_trans, ice_state); any_state |= (1 << ice_state); if (ice_state != STATE (COMPLETE)) all_completed = FALSE; @@ -922,40 +1037,93 @@ _collate_peer_connection_states (GstWebRTCBin * webrtc) #define DTLS_STATE(v) GST_WEBRTC_DTLS_TRANSPORT_STATE_ ## v GstWebRTCICEConnectionState any_ice_state = 0; GstWebRTCDTLSTransportState any_dtls_state = 0; + gboolean ice_all_new_or_closed = TRUE; + gboolean dtls_all_new_or_closed = TRUE; + gboolean ice_all_new_connecting_or_checking = TRUE; + gboolean dtls_all_new_connecting_or_checking = TRUE; + gboolean ice_all_connected_completed_or_closed = TRUE; + gboolean dtls_all_connected_completed_or_closed = TRUE; int i; for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *rtp_trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + g_ptr_array_index (webrtc->priv->transceivers, i); WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans); TransportStream *stream = trans->stream; GstWebRTCDTLSTransport *transport, *rtcp_transport; - GstWebRTCICEGatheringState ice_state; + GstWebRTCICEConnectionState ice_state; GstWebRTCDTLSTransportState dtls_state; gboolean rtcp_mux = FALSE; - if (rtp_trans->stopped) + if (rtp_trans->stopped) { + GST_TRACE_OBJECT (webrtc, "transceiver %p stopped", rtp_trans); continue; - if (!rtp_trans->mid) + } + if (!rtp_trans->mid) { + GST_TRACE_OBJECT (webrtc, "transceiver %p has no mid", rtp_trans); continue; + } g_object_get (stream, "rtcp-mux", &rtcp_mux, NULL); transport = webrtc_transceiver_get_dtls_transport (rtp_trans); /* get transport state */ g_object_get (transport, "state", &dtls_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p DTLS state: 0x%x", rtp_trans, + dtls_state); any_dtls_state |= (1 << dtls_state); + + if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED)) + dtls_all_new_or_closed = FALSE; + if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CONNECTING)) + dtls_all_new_connecting_or_checking = FALSE; + if (dtls_state != DTLS_STATE (CONNECTED) + && dtls_state != DTLS_STATE (CLOSED)) + dtls_all_connected_completed_or_closed = FALSE; + g_object_get (transport->transport, "state", &ice_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p ICE state: 0x%x", rtp_trans, + ice_state); any_ice_state |= (1 << ice_state); + if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED)) + ice_all_new_or_closed = FALSE; + if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING)) + ice_all_new_connecting_or_checking = FALSE; + if (ice_state != ICE_STATE (CONNECTED) && ice_state != ICE_STATE (COMPLETED) + && ice_state != ICE_STATE (CLOSED)) + ice_all_connected_completed_or_closed = FALSE; + rtcp_transport = webrtc_transceiver_get_rtcp_dtls_transport (rtp_trans); if (!rtcp_mux && rtcp_transport && rtcp_transport != transport) { g_object_get (rtcp_transport, "state", &dtls_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p RTCP DTLS state: 0x%x", + rtp_trans, dtls_state); any_dtls_state |= (1 << dtls_state); + + if (dtls_state != DTLS_STATE (NEW) && dtls_state != DTLS_STATE (CLOSED)) + dtls_all_new_or_closed = FALSE; + if (dtls_state != DTLS_STATE (NEW) + && dtls_state != DTLS_STATE (CONNECTING)) + dtls_all_new_connecting_or_checking = FALSE; + if (dtls_state != DTLS_STATE (CONNECTED) + && dtls_state != DTLS_STATE (CLOSED)) + dtls_all_connected_completed_or_closed = FALSE; + g_object_get (rtcp_transport->transport, "state", &ice_state, NULL); + GST_TRACE_OBJECT (webrtc, "transceiver %p RTCP ICE state: 0x%x", + rtp_trans, ice_state); any_ice_state |= (1 << ice_state); + + if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CLOSED)) + ice_all_new_or_closed = FALSE; + if (ice_state != ICE_STATE (NEW) && ice_state != ICE_STATE (CHECKING)) + ice_all_new_connecting_or_checking = FALSE; + if (ice_state != ICE_STATE (CONNECTED) + && ice_state != ICE_STATE (COMPLETED) + && ice_state != ICE_STATE (CLOSED)) + ice_all_connected_completed_or_closed = FALSE; } } @@ -978,57 +1146,48 @@ _collate_peer_connection_states (GstWebRTCBin * webrtc) return STATE (FAILED); } - /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the connecting - * or checking state and none of them is in the failed state. */ - if (any_ice_state & (1 << ICE_STATE (CHECKING))) { - GST_TRACE_OBJECT (webrtc, "returning connecting"); - return STATE (CONNECTING); - } - if (any_dtls_state & (1 << DTLS_STATE (CONNECTING))) { - GST_TRACE_OBJECT (webrtc, "returning connecting"); - return STATE (CONNECTING); - } - /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the disconnected - * state and none of them are in the failed or connecting or checking state. */ + * state. */ if (any_ice_state & (1 << ICE_STATE (DISCONNECTED))) { GST_TRACE_OBJECT (webrtc, "returning disconnected"); return STATE (DISCONNECTED); } - /* All RTCIceTransport's and RTCDtlsTransport's are in the connected, - * completed or closed state and at least of them is in the connected or - * completed state. */ - if (!(any_ice_state & ~(1 << ICE_STATE (CONNECTED) | 1 << - ICE_STATE (COMPLETED) | 1 << ICE_STATE (CLOSED))) - && !(any_dtls_state & ~(1 << DTLS_STATE (CONNECTED) | 1 << - DTLS_STATE (CLOSED))) - && (any_ice_state & (1 << ICE_STATE (CONNECTED) | 1 << - ICE_STATE (COMPLETED)) - || any_dtls_state & (1 << DTLS_STATE (CONNECTED)))) { + /* All RTCIceTransports and RTCDtlsTransports are in the new or closed + * state, or there are no transports. */ + if ((dtls_all_new_or_closed && ice_all_new_or_closed) + || webrtc->priv->transceivers->len == 0) { + GST_TRACE_OBJECT (webrtc, "returning new"); + return STATE (NEW); + } + + /* All RTCIceTransports and RTCDtlsTransports are in the new, connecting + * or checking state. */ + if (dtls_all_new_connecting_or_checking && ice_all_new_connecting_or_checking) { + GST_TRACE_OBJECT (webrtc, "returning connecting"); + return STATE (CONNECTING); + } + + /* All RTCIceTransports and RTCDtlsTransports are in the connected, + * completed or closed state. */ + if (dtls_all_connected_completed_or_closed + && ice_all_connected_completed_or_closed) { GST_TRACE_OBJECT (webrtc, "returning connected"); return STATE (CONNECTED); } - /* Any of the RTCIceTransport's or RTCDtlsTransport's are in the new state - * and none of the transports are in the connecting, checking, failed or - * disconnected state, or all transports are in the closed state. */ - if (!(any_ice_state & ~(1 << ICE_STATE (CLOSED)))) { - GST_TRACE_OBJECT (webrtc, "returning new"); - return STATE (NEW); - } - if ((any_ice_state & (1 << ICE_STATE (NEW)) - || any_dtls_state & (1 << DTLS_STATE (NEW))) - && !(any_ice_state & (1 << ICE_STATE (CHECKING) | 1 << ICE_STATE (FAILED) - | (1 << ICE_STATE (DISCONNECTED)))) - && !(any_dtls_state & (1 << DTLS_STATE (CONNECTING) | 1 << - DTLS_STATE (FAILED)))) { - GST_TRACE_OBJECT (webrtc, "returning new"); - return STATE (NEW); + /* FIXME: Unspecified state that happens for us */ + if ((dtls_all_new_connecting_or_checking + || dtls_all_connected_completed_or_closed) + && (ice_all_new_connecting_or_checking + || ice_all_connected_completed_or_closed)) { + GST_TRACE_OBJECT (webrtc, "returning connecting"); + return STATE (CONNECTING); } - GST_FIXME_OBJECT (webrtc, "Undefined situation detected, returning new"); - return STATE (NEW); + GST_FIXME_OBJECT (webrtc, + "Undefined situation detected, returning old state"); + return webrtc->peer_connection_state; #undef DTLS_STATE #undef ICE_STATE #undef STATE @@ -1042,6 +1201,19 @@ _update_ice_gathering_state_task (GstWebRTCBin * webrtc, gpointer data) new_state = _collate_ice_gathering_states (webrtc); + /* If the new state is complete, before we update the public state, + * check if anyone published more ICE candidates while we were collating + * and stop if so, because it means there's a new later + * ice_gathering_state_task queued */ + if (new_state == GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE) { + ICE_LOCK (webrtc); + if (webrtc->priv->pending_local_ice_candidates->len != 0) { + /* ICE candidates queued for emissiong -> we're gathering, not complete */ + new_state = GST_WEBRTC_ICE_GATHERING_STATE_GATHERING; + } + ICE_UNLOCK (webrtc); + } + if (new_state != webrtc->ice_gathering_state) { gchar *old_s, *new_s; @@ -1065,7 +1237,7 @@ static void _update_ice_gathering_state (GstWebRTCBin * webrtc) { gst_webrtc_bin_enqueue_task (webrtc, _update_ice_gathering_state_task, NULL, - NULL); + NULL, NULL); } static void @@ -1100,7 +1272,7 @@ static void _update_ice_connection_state (GstWebRTCBin * webrtc) { gst_webrtc_bin_enqueue_task (webrtc, _update_ice_connection_state_task, NULL, - NULL); + NULL, NULL); } static void @@ -1135,7 +1307,7 @@ static void _update_peer_connection_state (GstWebRTCBin * webrtc) { gst_webrtc_bin_enqueue_task (webrtc, _update_peer_connection_state_task, - NULL, NULL); + NULL, NULL, NULL); } static gboolean @@ -1147,16 +1319,23 @@ _all_sinks_have_caps (GstWebRTCBin * webrtc) GST_OBJECT_LOCK (webrtc); l = GST_ELEMENT (webrtc)->pads; for (; l; l = g_list_next (l)) { + GstWebRTCBinPad *wpad; + if (!GST_IS_WEBRTC_BIN_PAD (l->data)) continue; - if (!GST_WEBRTC_BIN_PAD (l->data)->received_caps) + + wpad = GST_WEBRTC_BIN_PAD (l->data); + if (GST_PAD_DIRECTION (l->data) == GST_PAD_SINK && !wpad->received_caps + && (!wpad->trans || !wpad->trans->stopped)) { goto done; + } } l = webrtc->priv->pending_pads; for (; l; l = g_list_next (l)) { - if (!GST_IS_WEBRTC_BIN_PAD (l->data)) + if (!GST_IS_WEBRTC_BIN_PAD (l->data)) { goto done; + } } res = TRUE; @@ -1187,10 +1366,6 @@ _check_if_negotiation_is_needed (GstWebRTCBin * webrtc) * FIXME */ /* FIXME: emit when input caps/format changes? */ - /* If connection has created any RTCDataChannel's, and no m= section has - * been negotiated yet for data, return "true". - * FIXME */ - if (!webrtc->current_local_description) { GST_LOG_OBJECT (webrtc, "no local description set"); return TRUE; @@ -1201,12 +1376,22 @@ _check_if_negotiation_is_needed (GstWebRTCBin * webrtc) return TRUE; } + /* If connection has created any RTCDataChannel's, and no m= section has + * been negotiated yet for data, return "true". */ + if (webrtc->priv->data_channels->len > 0) { + if (_message_get_datachannel_index (webrtc->current_local_description-> + sdp) >= G_MAXUINT) { + GST_LOG_OBJECT (webrtc, + "no data channel media section and have %u " "transports", + webrtc->priv->data_channels->len); + return TRUE; + } + } + for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *trans; - trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + trans = g_ptr_array_index (webrtc->priv->transceivers, i); if (trans->stopped) { /* FIXME: If t is stopped and is associated with an m= section according to @@ -1219,9 +1404,9 @@ _check_if_negotiation_is_needed (GstWebRTCBin * webrtc) const GstSDPMedia *media; GstWebRTCRTPTransceiverDirection local_dir, remote_dir; - if (trans->mline == -1) { - GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT, - i, trans); + if (trans->mline == -1 || trans->mid == NULL) { + GST_LOG_OBJECT (webrtc, "unassociated transceiver %i %" GST_PTR_FORMAT + " mid %s", i, trans, trans->mid); return TRUE; } /* internal inconsistency */ @@ -1251,8 +1436,26 @@ _check_if_negotiation_is_needed (GstWebRTCBin * webrtc) * nor answer matches t's direction, return "true". */ if (local_dir != trans->direction && remote_dir != trans->direction) { - GST_LOG_OBJECT (webrtc, - "transceiver direction doesn't match description"); + gchar *local_str, *remote_str, *dir_str; + + local_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + local_dir); + remote_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + remote_dir); + dir_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + trans->direction); + + GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match " + "description (local %s remote %s)", dir_str, local_str, + remote_str); + + g_free (dir_str); + g_free (local_str); + g_free (remote_str); + return TRUE; } } else if (webrtc->current_local_description->type == @@ -1268,8 +1471,30 @@ _check_if_negotiation_is_needed (GstWebRTCBin * webrtc) intersect_dir = _intersect_answer_directions (remote_dir, local_dir); if (intersect_dir != trans->direction) { - GST_LOG_OBJECT (webrtc, - "transceiver direction doesn't match description"); + gchar *local_str, *remote_str, *inter_str, *dir_str; + + local_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + local_dir); + remote_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + remote_dir); + dir_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + trans->direction); + inter_str = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + intersect_dir); + + GST_LOG_OBJECT (webrtc, "transceiver direction (%s) doesn't match " + "description intersected direction %s (local %s remote %s)", + dir_str, local_str, inter_str, remote_str); + + g_free (dir_str); + g_free (local_str); + g_free (remote_str); + g_free (inter_str); + return TRUE; } } @@ -1318,25 +1543,36 @@ _update_need_negotiation (GstWebRTCBin * webrtc) /* Queue a task to check connection's [[ needNegotiation]] slot and, if still * true, fire a simple event named negotiationneeded at connection. */ gst_webrtc_bin_enqueue_task (webrtc, _check_need_negotiation_task, NULL, - NULL); + NULL, NULL); } static GstCaps * -_find_codec_preferences (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * trans, - GstPadDirection direction, guint media_idx) +_find_codec_preferences (GstWebRTCBin * webrtc, + GstWebRTCRTPTransceiver * rtp_trans, GstPadDirection direction, + guint media_idx) { + WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans; GstCaps *ret = NULL; - GST_LOG_OBJECT (webrtc, "retreiving codec preferences from %" GST_PTR_FORMAT, + GST_LOG_OBJECT (webrtc, "retrieving codec preferences from %" GST_PTR_FORMAT, trans); - if (trans && trans->codec_preferences) { + if (rtp_trans && rtp_trans->codec_preferences) { GST_LOG_OBJECT (webrtc, "Using codec preferences: %" GST_PTR_FORMAT, - trans->codec_preferences); - ret = gst_caps_ref (trans->codec_preferences); + rtp_trans->codec_preferences); + ret = gst_caps_ref (rtp_trans->codec_preferences); } else { - GstWebRTCBinPad *pad = _find_pad_for_mline (webrtc, direction, media_idx); - if (pad) { + GstWebRTCBinPad *pad = NULL; + + /* try to find a pad */ + if (!trans + || !(pad = _find_pad_for_transceiver (webrtc, direction, rtp_trans))) + pad = _find_pad_for_mline (webrtc, direction, media_idx); + + if (!pad) { + if (trans && trans->last_configured_caps) + ret = gst_caps_ref (trans->last_configured_caps); + } else { GstCaps *caps = NULL; if (pad->received_caps) { @@ -1349,12 +1585,20 @@ _find_codec_preferences (GstWebRTCBin * webrtc, GstWebRTCRTPTransceiver * trans, GST_LOG_OBJECT (webrtc, "Using peer query caps: %" GST_PTR_FORMAT, caps); } - if (caps) + if (caps) { + if (trans) + gst_caps_replace (&trans->last_configured_caps, caps); + ret = caps; + } + gst_object_unref (pad); } } + if (!ret) + GST_DEBUG_OBJECT (trans, "Could not find caps for mline %u", media_idx); + return ret; } @@ -1380,7 +1624,7 @@ _add_supported_attributes_to_caps (GstWebRTCBin * webrtc, /*if (!gst_structure_has_field (s, "rtcp-fb-transport-cc")) gst_structure_set (s, "rtcp-fb-nack-pli", G_TYPE_BOOLEAN, TRUE, NULL); */ - /* FIXME: codec-specific paramters? */ + /* FIXME: codec-specific parameters? */ } return ret; @@ -1423,8 +1667,10 @@ _create_webrtc_transceiver (GstWebRTCBin * webrtc, rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans); rtp_trans->direction = direction; rtp_trans->mline = mline; + /* FIXME: We don't support stopping transceiver yet so they're always not stopped */ + rtp_trans->stopped = FALSE; - g_array_append_val (webrtc->priv->transceivers, trans); + g_ptr_array_add (webrtc->priv->transceivers, trans); gst_object_unref (sender); gst_object_unref (receiver); @@ -1481,7 +1727,7 @@ _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id) ret = _create_transport_channel (webrtc, session_id); gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->send_bin)); gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (ret->receive_bin)); - g_array_append_val (webrtc->priv->transports, ret); + g_ptr_array_add (webrtc->priv->transports, ret); pad_name = g_strdup_printf ("recv_rtcp_sink_%u", ret->session_id); if (!gst_element_link_pads (GST_ELEMENT (ret->receive_bin), "rtcp_src", @@ -1504,7 +1750,7 @@ _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id) /* this is called from the webrtc thread with the pc lock held */ static void -_on_data_channel_ready_state (GstWebRTCDataChannel * channel, +_on_data_channel_ready_state (WebRTCDataChannel * channel, GParamSpec * pspec, GstWebRTCBin * webrtc) { GstWebRTCDataChannelState ready_state; @@ -1516,13 +1762,12 @@ _on_data_channel_ready_state (GstWebRTCDataChannel * channel, gboolean found = FALSE; for (i = 0; i < webrtc->priv->pending_data_channels->len; i++) { - GstWebRTCDataChannel *c; + WebRTCDataChannel *c; - c = g_array_index (webrtc->priv->pending_data_channels, - GstWebRTCDataChannel *, i); + c = g_ptr_array_index (webrtc->priv->pending_data_channels, i); if (c == channel) { found = TRUE; - g_array_remove_index (webrtc->priv->pending_data_channels, i); + g_ptr_array_remove_index (webrtc->priv->pending_data_channels, i); break; } } @@ -1531,41 +1776,18 @@ _on_data_channel_ready_state (GstWebRTCDataChannel * channel, return; } - g_array_append_val (webrtc->priv->data_channels, channel); + g_ptr_array_add (webrtc->priv->data_channels, channel); g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0, gst_object_ref (channel)); } } -static void -_link_data_channel_to_sctp (GstWebRTCBin * webrtc, - GstWebRTCDataChannel * channel) -{ - if (webrtc->priv->sctp_transport && !channel->sctp_transport) { - gint id; - - g_object_get (channel, "id", &id, NULL); - - if (webrtc->priv->sctp_transport->association_established && id != -1) { - gchar *pad_name; - - gst_webrtc_data_channel_set_sctp_transport (channel, - webrtc->priv->sctp_transport); - pad_name = g_strdup_printf ("sink_%u", id); - if (!gst_element_link_pads (channel->appsrc, "src", - channel->sctp_transport->sctpenc, pad_name)) - g_warn_if_reached (); - g_free (pad_name); - } - } -} - static void _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad, GstWebRTCBin * webrtc) { - GstWebRTCDataChannel *channel; + WebRTCDataChannel *channel; guint stream_id; GstPad *sink_pad; @@ -1575,8 +1797,8 @@ _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad, PC_LOCK (webrtc); channel = _find_data_channel_for_id (webrtc, stream_id); if (!channel) { - channel = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, NULL); - channel->id = stream_id; + channel = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, NULL); + channel->parent.id = stream_id; channel->webrtcbin = webrtc; gst_bin_add (GST_BIN (webrtc), channel->appsrc); @@ -1585,9 +1807,9 @@ _on_sctpdec_pad_added (GstElement * sctpdec, GstPad * pad, gst_element_sync_state_with_parent (channel->appsrc); gst_element_sync_state_with_parent (channel->appsink); - _link_data_channel_to_sctp (webrtc, channel); + webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport); - g_array_append_val (webrtc->priv->pending_data_channels, channel); + g_ptr_array_add (webrtc->priv->pending_data_channels, channel); } g_signal_connect (channel, "notify::ready-state", @@ -1616,21 +1838,110 @@ _on_sctp_state_notify (GstWebRTCSCTPTransport * sctp, GParamSpec * pspec, GST_DEBUG_OBJECT (webrtc, "SCTP association established"); for (i = 0; i < webrtc->priv->data_channels->len; i++) { - GstWebRTCDataChannel *channel; + WebRTCDataChannel *channel; - channel = - g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, - i); + channel = g_ptr_array_index (webrtc->priv->data_channels, i); - _link_data_channel_to_sctp (webrtc, channel); + webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport); - if (!channel->negotiated && !channel->opened) - gst_webrtc_data_channel_start_negotiation (channel); + if (!channel->parent.negotiated && !channel->opened) + webrtc_data_channel_start_negotiation (channel); } PC_UNLOCK (webrtc); } } +/* Forward declaration so we can easily disconnect the signal handler */ +static void _on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport, + GParamSpec * pspec, GstWebRTCBin * webrtc); + +static void +_sctp_check_dtls_state_task (GstWebRTCBin * webrtc, gpointer unused) +{ + TransportStream *stream; + GstWebRTCDTLSTransport *transport; + GstWebRTCDTLSTransportState dtls_state; + GstWebRTCSCTPTransport *sctp_transport; + + stream = webrtc->priv->data_channel_transport; + transport = stream->transport; + + g_object_get (transport, "state", &dtls_state, NULL); + /* Not connected yet so just return */ + if (dtls_state != GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) { + GST_DEBUG_OBJECT (webrtc, + "Data channel DTLS connection is not ready yet: %d", dtls_state); + return; + } + + GST_DEBUG_OBJECT (webrtc, "Data channel DTLS connection is now ready"); + sctp_transport = webrtc->priv->sctp_transport; + + /* Not locked state anymore so this was already taken care of before */ + if (!gst_element_is_locked_state (sctp_transport->sctpdec)) + return; + + /* Start up the SCTP elements now that the DTLS connection is established */ + gst_element_set_locked_state (sctp_transport->sctpdec, FALSE); + gst_element_set_locked_state (sctp_transport->sctpenc, FALSE); + + gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpdec)); + gst_element_sync_state_with_parent (GST_ELEMENT (sctp_transport->sctpenc)); + + if (sctp_transport->sctpdec_block_id) { + GstPad *receive_srcpad; + + receive_srcpad = + gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin), + "data_src"); + gst_pad_remove_probe (receive_srcpad, sctp_transport->sctpdec_block_id); + + sctp_transport->sctpdec_block_id = 0; + gst_object_unref (receive_srcpad); + } + + g_signal_handlers_disconnect_by_func (transport, _on_sctp_notify_dtls_state, + webrtc); +} + +static void +_on_sctp_notify_dtls_state (GstWebRTCDTLSTransport * transport, + GParamSpec * pspec, GstWebRTCBin * webrtc) +{ + GstWebRTCDTLSTransportState dtls_state; + + g_object_get (transport, "state", &dtls_state, NULL); + + GST_TRACE_OBJECT (webrtc, "Data channel DTLS state changed to %d", + dtls_state); + + /* Connected now, so schedule a task to update the state of the SCTP + * elements */ + if (dtls_state == GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED) { + gst_webrtc_bin_enqueue_task (webrtc, + (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL); + } +} + +static GstPadProbeReturn +sctp_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused) +{ + /* Drop all events: we don't care about them and don't want to block on + * them. Sticky events would be forwarded again later once we unblock + * and we don't want to forward them here already because that might + * cause a spurious GST_FLOW_FLUSHING */ + if (GST_IS_EVENT (info->data)) + return GST_PAD_PROBE_DROP; + + /* But block on any actual data-flow so we don't accidentally send that + * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything + * to silently stop. + */ + GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data); + + return GST_PAD_PROBE_OK; +} + static TransportStream * _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id) { @@ -1645,7 +1956,7 @@ _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id) stream = _create_transport_channel (webrtc, session_id); gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->send_bin)); gst_bin_add (GST_BIN (webrtc), GST_ELEMENT (stream->receive_bin)); - g_array_append_val (webrtc->priv->transports, stream); + g_ptr_array_add (webrtc->priv->transports, stream); } webrtc->priv->data_channel_transport = stream; @@ -1658,6 +1969,11 @@ _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id) g_object_ref (webrtc->priv->data_channel_transport->transport); sctp_transport->webrtcbin = webrtc; + /* Don't automatically start SCTP elements as part of webrtcbin. We + * need to delay this until the DTLS transport is fully connected! */ + gst_element_set_locked_state (sctp_transport->sctpdec, TRUE); + gst_element_set_locked_state (sctp_transport->sctpenc, TRUE); + gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpdec); gst_bin_add (GST_BIN (webrtc), sctp_transport->sctpenc); } @@ -1667,6 +1983,18 @@ _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id) g_signal_connect (sctp_transport, "notify::state", G_CALLBACK (_on_sctp_state_notify), webrtc); + if (sctp_transport->sctpdec_block_id == 0) { + GstPad *receive_srcpad; + receive_srcpad = + gst_element_get_static_pad (GST_ELEMENT (stream->receive_bin), + "data_src"); + sctp_transport->sctpdec_block_id = + gst_pad_add_probe (receive_srcpad, + GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, + (GstPadProbeCallback) sctp_pad_block, NULL, NULL); + gst_object_unref (receive_srcpad); + } + if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), "data_src", GST_ELEMENT (sctp_transport->sctpdec), "sink")) g_warn_if_reached (); @@ -1676,23 +2004,27 @@ _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id) g_warn_if_reached (); for (i = 0; i < webrtc->priv->data_channels->len; i++) { - GstWebRTCDataChannel *channel; + WebRTCDataChannel *channel; - channel = - g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, - i); + channel = g_ptr_array_index (webrtc->priv->data_channels, i); - _link_data_channel_to_sctp (webrtc, channel); + webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport); } gst_element_sync_state_with_parent (GST_ELEMENT (stream->send_bin)); gst_element_sync_state_with_parent (GST_ELEMENT (stream->receive_bin)); if (!webrtc->priv->sctp_transport) { - gst_element_sync_state_with_parent (GST_ELEMENT - (sctp_transport->sctpdec)); - gst_element_sync_state_with_parent (GST_ELEMENT - (sctp_transport->sctpenc)); + /* Connect to the notify::state signal to get notified when the DTLS + * connection is established. Only then can we start the SCTP elements */ + g_signal_connect (stream->transport, "notify::state", + G_CALLBACK (_on_sctp_notify_dtls_state), webrtc); + + /* As this would be racy otherwise, also schedule a task that checks the + * current state of the connection already without getting the signal + * called */ + gst_webrtc_bin_enqueue_task (webrtc, + (GstWebRTCBinFunc) _sctp_check_dtls_state_task, NULL, NULL, NULL); } webrtc->priv->sctp_transport = sctp_transport; @@ -1946,8 +2278,8 @@ _add_fingerprint_to_media (GstWebRTCDTLSTransport * transport, static gboolean sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, GstWebRTCRTPTransceiver * trans, GstWebRTCSDPType type, guint media_idx, - GString * bundled_mids, guint bundle_idx, gboolean bundle_only, - GArray * reserved_pts) + GString * bundled_mids, guint bundle_idx, gchar * bundle_ufrag, + gchar * bundle_pwd, GArray * reserved_pts) { /* TODO: * rtp header extensions @@ -1959,19 +2291,48 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, * dtls fingerprints * multiple dtls fingerprints https://tools.ietf.org/html/draft-ietf-mmusic-4572-update-05 */ - gchar *direction, *sdp_mid; + GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc); + gchar *direction, *sdp_mid, *ufrag, *pwd; + gboolean bundle_only; GstCaps *caps; int i; - /* "An m= section is generated for each RtpTransceiver that has been added - * to the Bin, excluding any stopped RtpTransceivers." */ - if (trans->stopped) - return FALSE; if (trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE || trans->direction == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) return FALSE; - gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0); + g_assert (trans->mline == -1 || trans->mline == media_idx); + + bundle_only = bundled_mids && bundle_idx != media_idx + && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE; + + /* mandated by JSEP */ + gst_sdp_media_add_attribute (media, "setup", "actpass"); + + /* FIXME: deal with ICE restarts */ + if (last_offer && trans->mline != -1 && trans->mid) { + ufrag = g_strdup (_media_get_ice_ufrag (last_offer, trans->mline)); + pwd = g_strdup (_media_get_ice_pwd (last_offer, trans->mline)); + GST_DEBUG_OBJECT (trans, "%u Using previous ice parameters", media_idx); + } else { + GST_DEBUG_OBJECT (trans, + "%u Generating new ice parameters mline %i, mid %s", media_idx, + trans->mline, trans->mid); + if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) { + _generate_ice_credentials (&ufrag, &pwd); + } else { + g_assert (bundle_ufrag && bundle_pwd); + ufrag = g_strdup (bundle_ufrag); + pwd = g_strdup (bundle_pwd); + } + } + + gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag); + gst_sdp_media_add_attribute (media, "ice-pwd", pwd); + g_free (ufrag); + g_free (pwd); + + gst_sdp_media_set_port_info (media, bundle_only || trans->stopped ? 0 : 9, 0); gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF"); gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0); @@ -1998,9 +2359,6 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, caps = _add_supported_attributes_to_caps (webrtc, WEBRTC_TRANSCEIVER (trans), caps); - } else if (type == GST_WEBRTC_SDP_TYPE_ANSWER) { - caps = _find_codec_preferences (webrtc, trans, GST_PAD_SRC, media_idx); - /* FIXME: add rtcp-fb paramaters */ } else { g_assert_not_reached (); } @@ -2060,10 +2418,18 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, _media_add_ssrcs (media, caps, webrtc, WEBRTC_TRANSCEIVER (trans)); /* Some identifier; we also add the media name to it so it's identifiable */ - sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media), - webrtc->priv->media_counter++); - gst_sdp_media_add_attribute (media, "mid", sdp_mid); - g_free (sdp_mid); + if (trans->mid) { + gst_sdp_media_add_attribute (media, "mid", trans->mid); + } else { + sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media), + webrtc->priv->media_counter++); + gst_sdp_media_add_attribute (media, "mid", sdp_mid); + g_free (sdp_mid); + } + + /* TODO: + * - add a=candidate lines for gathered candidates + */ if (trans->sender) { if (!trans->sender->transport) { @@ -2079,6 +2445,13 @@ sdp_media_from_transceiver (GstWebRTCBin * webrtc, GstSDPMedia * media, _add_fingerprint_to_media (trans->sender->transport, media); } + if (bundled_mids) { + const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); + + g_assert (mid); + g_string_append_printf (bundled_mids, " %s", mid); + } + gst_caps_unref (caps); return TRUE; @@ -2112,25 +2485,122 @@ gather_reserved_pts (GstWebRTCBin * webrtc) return reserved_pts; } -/* TODO: use the options argument */ -static GstSDPMessage * -_create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) +static gboolean +_add_data_channel_offer (GstWebRTCBin * webrtc, GstSDPMessage * msg, + GstSDPMedia * media, GString * bundled_mids, guint bundle_idx, + gchar * bundle_ufrag, gchar * bundle_pwd) { - GstSDPMessage *ret; - int i; - GString *bundled_mids = NULL; - gchar *bundle_ufrag = NULL; - gchar *bundle_pwd = NULL; - GArray *reserved_pts = NULL; + GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc); + gchar *ufrag, *pwd, *sdp_mid; + gboolean bundle_only = bundled_mids + && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE + && gst_sdp_message_medias_len (msg) != bundle_idx; + guint last_data_index = G_MAXUINT; - gst_sdp_message_new (&ret); + /* add data channel support */ + if (webrtc->priv->data_channels->len == 0) + return FALSE; + + if (last_offer) { + last_data_index = _message_get_datachannel_index (last_offer); + if (last_data_index < G_MAXUINT) { + g_assert (last_data_index < gst_sdp_message_medias_len (last_offer)); + /* XXX: is this always true when recycling transceivers? + * i.e. do we always put the data channel in the same mline */ + g_assert (last_data_index == gst_sdp_message_medias_len (msg)); + } + } + + /* mandated by JSEP */ + gst_sdp_media_add_attribute (media, "setup", "actpass"); + + /* FIXME: only needed when restarting ICE */ + if (last_offer && last_data_index < G_MAXUINT) { + ufrag = g_strdup (_media_get_ice_ufrag (last_offer, last_data_index)); + pwd = g_strdup (_media_get_ice_pwd (last_offer, last_data_index)); + } else { + if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) { + _generate_ice_credentials (&ufrag, &pwd); + } else { + ufrag = g_strdup (bundle_ufrag); + pwd = g_strdup (bundle_pwd); + } + } + gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag); + gst_sdp_media_add_attribute (media, "ice-pwd", pwd); + g_free (ufrag); + g_free (pwd); + + gst_sdp_media_set_media (media, "application"); + gst_sdp_media_set_port_info (media, bundle_only ? 0 : 9, 0); + gst_sdp_media_set_proto (media, "UDP/DTLS/SCTP"); + gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0); + gst_sdp_media_add_format (media, "webrtc-datachannel"); + + if (bundle_idx != gst_sdp_message_medias_len (msg)) + gst_sdp_media_add_attribute (media, "bundle-only", NULL); + + if (last_offer && last_data_index < G_MAXUINT) { + const GstSDPMedia *last_data_media; + const gchar *mid; + + last_data_media = gst_sdp_message_get_media (last_offer, last_data_index); + mid = gst_sdp_media_get_attribute_val (last_data_media, "mid"); + + gst_sdp_media_add_attribute (media, "mid", mid); + } else { + sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (media), + webrtc->priv->media_counter++); + gst_sdp_media_add_attribute (media, "mid", sdp_mid); + g_free (sdp_mid); + } + + if (bundled_mids) { + const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); + + g_assert (mid); + g_string_append_printf (bundled_mids, " %s", mid); + } + + /* FIXME: negotiate this properly */ + gst_sdp_media_add_attribute (media, "sctp-port", "5000"); + + _get_or_create_data_channel_transports (webrtc, + bundled_mids ? 0 : webrtc->priv->transceivers->len); + _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, media); + + return TRUE; +} + +/* TODO: use the options argument */ +static GstSDPMessage * +_create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) +{ + GstSDPMessage *ret; + GString *bundled_mids = NULL; + gchar *bundle_ufrag = NULL; + gchar *bundle_pwd = NULL; + GArray *reserved_pts = NULL; + GstSDPMessage *last_offer = _get_latest_self_generated_sdp (webrtc); + GList *seen_transceivers = NULL; + guint media_idx = 0; + int i; + + gst_sdp_message_new (&ret); gst_sdp_message_set_version (ret, "0"); { - /* FIXME: session id and version need special handling depending on the state we're in */ - gchar *sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID); - gst_sdp_message_set_origin (ret, "-", sess_id, "0", "IN", "IP4", "0.0.0.0"); + gchar *v, *sess_id; + v = g_strdup_printf ("%u", webrtc->priv->offer_count++); + if (last_offer) { + const GstSDPOrigin *origin = gst_sdp_message_get_origin (last_offer); + sess_id = g_strdup (origin->sess_id); + } else { + sess_id = g_strdup_printf ("%" G_GUINT64_FORMAT, RANDOM_SESSION_ID); + } + gst_sdp_message_set_origin (ret, "-", sess_id, v, "IN", "IP4", "0.0.0.0"); g_free (sess_id); + g_free (v); } gst_sdp_message_set_session_name (ret, "-"); gst_sdp_message_add_time (ret, "0", "0", NULL); @@ -2143,53 +2613,115 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) } if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) { - _generate_ice_credentials (&bundle_ufrag, &bundle_pwd); + GStrv last_bundle = NULL; + guint bundle_media_index; + reserved_pts = gather_reserved_pts (webrtc); + if (last_offer && _parse_bundle (last_offer, &last_bundle) && last_bundle + && last_bundle && last_bundle[0] + && _get_bundle_index (last_offer, last_bundle, &bundle_media_index)) { + bundle_ufrag = + g_strdup (_media_get_ice_ufrag (last_offer, bundle_media_index)); + bundle_pwd = + g_strdup (_media_get_ice_pwd (last_offer, bundle_media_index)); + } else { + _generate_ice_credentials (&bundle_ufrag, &bundle_pwd); + } + + g_strfreev (last_bundle); + } + + /* FIXME: recycle transceivers */ + + /* Fill up the renegotiated streams first */ + if (last_offer) { + for (i = 0; i < gst_sdp_message_medias_len (last_offer); i++) { + GstWebRTCRTPTransceiver *trans = NULL; + const GstSDPMedia *last_media; + + last_media = gst_sdp_message_get_media (last_offer, i); + + if (g_strcmp0 (gst_sdp_media_get_media (last_media), "audio") == 0 + || g_strcmp0 (gst_sdp_media_get_media (last_media), "video") == 0) { + const gchar *last_mid; + int j; + last_mid = gst_sdp_media_get_attribute_val (last_media, "mid"); + + for (j = 0; j < webrtc->priv->transceivers->len; j++) { + trans = g_ptr_array_index (webrtc->priv->transceivers, j); + + if (trans->mid && g_strcmp0 (trans->mid, last_mid) == 0) { + GstSDPMedia *media; + + g_assert (!g_list_find (seen_transceivers, trans)); + + GST_LOG_OBJECT (webrtc, "using previous negotiatied transceiver %" + GST_PTR_FORMAT " with mid %s into media index %u", trans, + trans->mid, media_idx); + + /* FIXME: deal with format changes */ + gst_sdp_media_copy (last_media, &media); + _media_replace_direction (media, trans->direction); + + if (bundled_mids) { + const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); + + g_assert (mid); + g_string_append_printf (bundled_mids, " %s", mid); + } + + gst_sdp_message_add_media (ret, media); + media_idx++; + + gst_sdp_media_free (media); + seen_transceivers = g_list_prepend (seen_transceivers, trans); + break; + } + } + } else if (g_strcmp0 (gst_sdp_media_get_media (last_media), + "application") == 0) { + GstSDPMedia media = { 0, }; + gst_sdp_media_init (&media); + if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0, + bundle_ufrag, bundle_pwd)) { + gst_sdp_message_add_media (ret, &media); + media_idx++; + } else { + gst_sdp_media_uninit (&media); + } + } + } } - /* for each rtp transceiver */ + /* add any extra streams */ for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *trans; GstSDPMedia media = { 0, }; - gchar *ufrag, *pwd; - gboolean bundle_only = bundled_mids - && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE - && i != 0; - trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + trans = g_ptr_array_index (webrtc->priv->transceivers, i); + + /* don't add transceivers twice */ + if (g_list_find (seen_transceivers, trans)) + continue; + + /* don't add stopped transceivers */ + if (trans->stopped) + continue; gst_sdp_media_init (&media); - /* mandated by JSEP */ - gst_sdp_media_add_attribute (&media, "setup", "actpass"); - /* FIXME: only needed when restarting ICE */ if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) { reserved_pts = g_array_new (FALSE, FALSE, sizeof (guint)); - _generate_ice_credentials (&ufrag, &pwd); - } else { - ufrag = g_strdup (bundle_ufrag); - pwd = g_strdup (bundle_pwd); } - gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag); - gst_sdp_media_add_attribute (&media, "ice-pwd", pwd); - g_free (ufrag); - g_free (pwd); - - g_assert (reserved_pts != NULL); + GST_LOG_OBJECT (webrtc, "adding transceiver %" GST_PTR_FORMAT " at media " + "index %u", trans, media_idx); if (sdp_media_from_transceiver (webrtc, &media, trans, - GST_WEBRTC_SDP_TYPE_OFFER, i, bundled_mids, 0, bundle_only, - reserved_pts)) { - if (bundled_mids) { - const gchar *mid = gst_sdp_media_get_attribute_val (&media, "mid"); - - g_assert (mid); - g_string_append_printf (bundled_mids, " %s", mid); - } + GST_WEBRTC_SDP_TYPE_OFFER, media_idx, bundled_mids, 0, bundle_ufrag, + bundle_pwd, reserved_pts)) { gst_sdp_message_add_media (ret, &media); + media_idx++; } else { gst_sdp_media_uninit (&media); } @@ -2197,62 +2729,28 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) { g_array_free (reserved_pts, TRUE); } + seen_transceivers = g_list_prepend (seen_transceivers, trans); } if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) { g_array_free (reserved_pts, TRUE); } - /* add data channel support */ - if (webrtc->priv->data_channels->len > 0) { + /* add a data channel if exists and not renegotiated */ + if (_message_get_datachannel_index (ret) == G_MAXUINT) { GstSDPMedia media = { 0, }; - gchar *ufrag, *pwd, *sdp_mid; - gboolean bundle_only = bundled_mids - && webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_MAX_BUNDLE - && webrtc->priv->transceivers->len != 0; - gst_sdp_media_init (&media); - /* mandated by JSEP */ - gst_sdp_media_add_attribute (&media, "setup", "actpass"); - - /* FIXME: only needed when restarting ICE */ - if (webrtc->bundle_policy == GST_WEBRTC_BUNDLE_POLICY_NONE) { - _generate_ice_credentials (&ufrag, &pwd); + if (_add_data_channel_offer (webrtc, ret, &media, bundled_mids, 0, + bundle_ufrag, bundle_pwd)) { + gst_sdp_message_add_media (ret, &media); + media_idx++; } else { - ufrag = g_strdup (bundle_ufrag); - pwd = g_strdup (bundle_pwd); + gst_sdp_media_uninit (&media); } - gst_sdp_media_add_attribute (&media, "ice-ufrag", ufrag); - gst_sdp_media_add_attribute (&media, "ice-pwd", pwd); - g_free (ufrag); - g_free (pwd); - - gst_sdp_media_set_media (&media, "application"); - gst_sdp_media_set_port_info (&media, bundle_only ? 0 : 9, 0); - gst_sdp_media_set_proto (&media, "UDP/DTLS/SCTP"); - gst_sdp_media_add_connection (&media, "IN", "IP4", "0.0.0.0", 0, 0); - gst_sdp_media_add_format (&media, "webrtc-datachannel"); - - if (bundle_only) - gst_sdp_media_add_attribute (&media, "bundle-only", NULL); - - sdp_mid = g_strdup_printf ("%s%u", gst_sdp_media_get_media (&media), - webrtc->priv->media_counter++); - gst_sdp_media_add_attribute (&media, "mid", sdp_mid); - if (bundled_mids) - g_string_append_printf (bundled_mids, " %s", sdp_mid); - g_free (sdp_mid); - - /* FIXME: negotiate this properly */ - gst_sdp_media_add_attribute (&media, "sctp-port", "5000"); - - _get_or_create_data_channel_transports (webrtc, - bundled_mids ? 0 : webrtc->priv->transceivers->len); - _add_fingerprint_to_media (webrtc->priv->sctp_transport->transport, &media); - - gst_sdp_message_add_media (ret, &media); } + g_assert (media_idx == gst_sdp_message_medias_len (ret)); + if (bundled_mids) { gchar *mids = g_string_free (bundled_mids, FALSE); @@ -2268,8 +2766,19 @@ _create_offer_task (GstWebRTCBin * webrtc, const GstStructure * options) /* FIXME: pre-emptively setup receiving elements when needed */ - /* XXX: only true for the initial offerer */ - g_object_set (webrtc->priv->ice, "controller", TRUE, NULL); + g_list_free (seen_transceivers); + + if (webrtc->priv->last_generated_answer) + gst_webrtc_session_description_free (webrtc->priv->last_generated_answer); + webrtc->priv->last_generated_answer = NULL; + if (webrtc->priv->last_generated_offer) + gst_webrtc_session_description_free (webrtc->priv->last_generated_offer); + { + GstSDPMessage *copy; + gst_sdp_message_copy (ret, ©); + webrtc->priv->last_generated_offer = + gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_OFFER, copy); + } return ret; } @@ -2396,6 +2905,8 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) GString *bundled_mids = NULL; gchar *bundle_ufrag = NULL; gchar *bundle_pwd = NULL; + GList *seen_transceivers = NULL; + GstSDPMessage *last_answer = _get_latest_self_generated_sdp (webrtc); if (!webrtc->pending_remote_description) { GST_ERROR_OBJECT (webrtc, @@ -2407,6 +2918,9 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) goto out; if (bundled) { + GStrv last_bundle = NULL; + guint bundle_media_index; + if (!_get_bundle_index (pending_remote->sdp, bundled, &bundle_idx)) { GST_ERROR_OBJECT (webrtc, "Bundle tag is %s but no media found matching", bundled[0]); @@ -2417,18 +2931,28 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) bundled_mids = g_string_new ("BUNDLE"); } - _generate_ice_credentials (&bundle_ufrag, &bundle_pwd); + if (last_answer && _parse_bundle (last_answer, &last_bundle) + && last_bundle && last_bundle[0] + && _get_bundle_index (last_answer, last_bundle, &bundle_media_index)) { + bundle_ufrag = + g_strdup (_media_get_ice_ufrag (last_answer, bundle_media_index)); + bundle_pwd = + g_strdup (_media_get_ice_pwd (last_answer, bundle_media_index)); + } else { + _generate_ice_credentials (&bundle_ufrag, &bundle_pwd); + } + + g_strfreev (last_bundle); } gst_sdp_message_new (&ret); - /* FIXME: session id and version need special handling depending on the state we're in */ gst_sdp_message_set_version (ret, "0"); { const GstSDPOrigin *offer_origin = gst_sdp_message_get_origin (pending_remote->sdp); - gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id, "0", "IN", - "IP4", "0.0.0.0"); + gst_sdp_message_set_origin (ret, "-", offer_origin->sess_id, + offer_origin->sess_version, "IN", "IP4", "0.0.0.0"); } gst_sdp_message_set_session_name (ret, "-"); @@ -2444,17 +2968,10 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) for (i = 0; i < gst_sdp_message_medias_len (pending_remote->sdp); i++) { GstSDPMedia *media = NULL; GstSDPMedia *offer_media; - GstWebRTCRTPTransceiver *rtp_trans = NULL; - WebRTCTransceiver *trans = NULL; - GstWebRTCRTPTransceiverDirection offer_dir, answer_dir; GstWebRTCDTLSSetup offer_setup, answer_setup; - GstCaps *offer_caps, *answer_caps = NULL; - guint j; - guint k; - gint target_pt = -1; - gint original_target_pt = -1; - guint target_ssrc = 0; + guint j, k; gboolean bundle_only; + const gchar *mid; offer_media = (GstSDPMedia *) gst_sdp_message_get_media (pending_remote->sdp, i); @@ -2468,13 +2985,19 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) gst_sdp_media_add_connection (media, "IN", "IP4", "0.0.0.0", 0, 0); { - /* FIXME: only needed when restarting ICE */ gchar *ufrag, *pwd; - if (!bundled) { - _generate_ice_credentials (&ufrag, &pwd); + + /* FIXME: deal with ICE restarts */ + if (last_answer && i < gst_sdp_message_medias_len (last_answer)) { + ufrag = g_strdup (_media_get_ice_ufrag (last_answer, i)); + pwd = g_strdup (_media_get_ice_pwd (last_answer, i)); } else { - ufrag = g_strdup (bundle_ufrag); - pwd = g_strdup (bundle_pwd); + if (!bundled) { + _generate_ice_credentials (&ufrag, &pwd); + } else { + ufrag = g_strdup (bundle_ufrag); + pwd = g_strdup (bundle_pwd); + } } gst_sdp_media_add_attribute (media, "ice-ufrag", ufrag); gst_sdp_media_add_attribute (media, "ice-pwd", pwd); @@ -2493,6 +3016,10 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) } } + mid = gst_sdp_media_get_attribute_val (media, "mid"); + /* XXX: not strictly required but a lot of functionality requires a mid */ + g_assert (mid); + /* set the a=setup: attribute */ offer_setup = _get_dtls_setup_from_media (offer_media); answer_setup = _intersect_dtls_setup (offer_setup); @@ -2511,13 +3038,6 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) "for webrtc-datachannel"); goto rejected; } - if (g_strcmp0 (gst_sdp_media_get_format (offer_media, 0), - "webrtc-datachannel") != 0) { - GST_WARNING_OBJECT (webrtc, - "format field of data channel m= line " - "is not \'webrtc-datachannel\'"); - goto rejected; - } sctp_port = _get_sctp_port_from_media (offer_media); if (sctp_port == -1) { GST_WARNING_OBJECT (webrtc, "media does not contain a sctp port"); @@ -2539,8 +3059,6 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) bundled_mids ? bundle_idx : i); if (bundled_mids) { - const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); - g_assert (mid); g_string_append_printf (bundled_mids, " %s", mid); } @@ -2549,62 +3067,95 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) media); } else if (g_strcmp0 (gst_sdp_media_get_media (offer_media), "audio") == 0 || g_strcmp0 (gst_sdp_media_get_media (offer_media), "video") == 0) { - gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF"); - - offer_caps = gst_caps_new_empty (); - for (j = 0; j < gst_sdp_media_formats_len (offer_media); j++) { - guint pt = atoi (gst_sdp_media_get_format (offer_media, j)); - GstCaps *caps; + GstCaps *offer_caps, *answer_caps = NULL; + GstWebRTCRTPTransceiver *rtp_trans = NULL; + WebRTCTransceiver *trans = NULL; + GstWebRTCRTPTransceiverDirection offer_dir, answer_dir; + gint target_pt = -1; + gint original_target_pt = -1; + guint target_ssrc = 0; - caps = gst_sdp_media_get_caps_from_media (offer_media, pt); + gst_sdp_media_set_proto (media, "UDP/TLS/RTP/SAVPF"); + offer_caps = _rtp_caps_from_media (offer_media); + + if (last_answer && i < gst_sdp_message_medias_len (last_answer) + && (rtp_trans = + _find_transceiver (webrtc, mid, + (FindTransceiverFunc) match_for_mid))) { + const GstSDPMedia *last_media = + gst_sdp_message_get_media (last_answer, i); + const gchar *last_mid = + gst_sdp_media_get_attribute_val (last_media, "mid"); + + /* FIXME: assumes no shenanigans with recycling transceivers */ + g_assert (g_strcmp0 (mid, last_mid) == 0); + + if (!answer_caps + && (rtp_trans->direction == + GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV + || rtp_trans->direction == + GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY)) + answer_caps = + _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, i); + if (!answer_caps + && (rtp_trans->direction == + GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV + || rtp_trans->direction == + GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY)) + answer_caps = + _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SRC, i); + if (!answer_caps) + answer_caps = _rtp_caps_from_media (last_media); + + /* XXX: In theory we're meant to use the sendrecv formats for the + * inactive direction however we don't know what that may be and would + * require asking outside what it expects to possibly send later */ + + GST_LOG_OBJECT (webrtc, "Found existing previously negotiated " + "transceiver %" GST_PTR_FORMAT " from mid %s for mline %u " + "using caps %" GST_PTR_FORMAT, rtp_trans, mid, i, answer_caps); + } else { + for (j = 0; j < webrtc->priv->transceivers->len; j++) { + GstCaps *trans_caps; - /* gst_sdp_media_get_caps_from_media() produces caps with name - * "application/x-unknown" which will fail intersection with - * "application/x-rtp" caps so mangle the returns caps to have the - * correct name here */ - for (k = 0; k < gst_caps_get_size (caps); k++) { - GstStructure *s = gst_caps_get_structure (caps, k); - gst_structure_set_name (s, "application/x-rtp"); - } + rtp_trans = g_ptr_array_index (webrtc->priv->transceivers, j); - gst_caps_append (offer_caps, caps); - } + if (g_list_find (seen_transceivers, rtp_trans)) { + /* Don't double allocate a transceiver to multiple mlines */ + rtp_trans = NULL; + continue; + } - for (j = 0; j < webrtc->priv->transceivers->len; j++) { - GstCaps *trans_caps; - - rtp_trans = - g_array_index (webrtc->priv->transceivers, - GstWebRTCRTPTransceiver *, j); - trans_caps = - _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j); - - GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT - " and %" GST_PTR_FORMAT, offer_caps, trans_caps); - - /* FIXME: technically this is a little overreaching as some fields we - * we can deal with not having and/or we may have unrecognized fields - * that we cannot actually support */ - if (trans_caps) { - answer_caps = gst_caps_intersect (offer_caps, trans_caps); - if (answer_caps && !gst_caps_is_empty (answer_caps)) { - GST_LOG_OBJECT (webrtc, - "found compatible transceiver %" GST_PTR_FORMAT - " for offer media %u", trans, i); - if (trans_caps) - gst_caps_unref (trans_caps); - break; - } else { - if (answer_caps) { - gst_caps_unref (answer_caps); - answer_caps = NULL; + trans_caps = + _find_codec_preferences (webrtc, rtp_trans, GST_PAD_SINK, j); + + GST_TRACE_OBJECT (webrtc, "trying to compare %" GST_PTR_FORMAT + " and %" GST_PTR_FORMAT, offer_caps, trans_caps); + + /* FIXME: technically this is a little overreaching as some fields we + * we can deal with not having and/or we may have unrecognized fields + * that we cannot actually support */ + if (trans_caps) { + answer_caps = gst_caps_intersect (offer_caps, trans_caps); + if (answer_caps && !gst_caps_is_empty (answer_caps)) { + GST_LOG_OBJECT (webrtc, + "found compatible transceiver %" GST_PTR_FORMAT + " for offer media %u", rtp_trans, i); + if (trans_caps) + gst_caps_unref (trans_caps); + break; + } else { + if (answer_caps) { + gst_caps_unref (answer_caps); + answer_caps = NULL; + } + if (trans_caps) + gst_caps_unref (trans_caps); + rtp_trans = NULL; } - if (trans_caps) - gst_caps_unref (trans_caps); + } else { rtp_trans = NULL; } - } else { - rtp_trans = NULL; } } @@ -2620,9 +3171,22 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) answer_caps = gst_caps_ref (offer_caps); } + if (gst_caps_is_empty (answer_caps)) { + GST_WARNING_OBJECT (webrtc, "Could not create caps for media"); + if (rtp_trans) + gst_object_unref (rtp_trans); + gst_caps_unref (answer_caps); + goto rejected; + } + + seen_transceivers = g_list_prepend (seen_transceivers, rtp_trans); + if (!rtp_trans) { trans = _create_webrtc_transceiver (webrtc, answer_dir, i); rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans); + + GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT + " for mline %u", trans, i); } else { trans = WEBRTC_TRANSCEIVER (rtp_trans); } @@ -2670,18 +3234,19 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) if (!trans->stream) { TransportStream *item; - if (bundled_mids) { - const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); - item = _get_or_create_transport_stream (webrtc, bundle_idx, FALSE); - - g_assert (mid); - g_string_append_printf (bundled_mids, " %s", mid); - } else { - item = _get_or_create_transport_stream (webrtc, i, FALSE); - } + item = + _get_or_create_transport_stream (webrtc, + bundled_mids ? bundle_idx : i, FALSE); webrtc_transceiver_set_transport (trans, item); } + if (bundled_mids) { + const gchar *mid = gst_sdp_media_get_attribute_val (media, "mid"); + + g_assert (mid); + g_string_append_printf (bundled_mids, " %s", mid); + } + /* set the a=fingerprint: for this transport */ _add_fingerprint_to_media (trans->stream->transport, media); @@ -2718,11 +3283,24 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options) /* FIXME: can we add not matched transceivers? */ /* XXX: only true for the initial offerer */ - g_object_set (webrtc->priv->ice, "controller", FALSE, NULL); + gst_webrtc_ice_set_is_controller (webrtc->priv->ice, FALSE); out: - if (bundled) - g_strfreev (bundled); + g_strfreev (bundled); + + g_list_free (seen_transceivers); + + if (webrtc->priv->last_generated_offer) + gst_webrtc_session_description_free (webrtc->priv->last_generated_offer); + webrtc->priv->last_generated_offer = NULL; + if (webrtc->priv->last_generated_answer) + gst_webrtc_session_description_free (webrtc->priv->last_generated_answer); + { + GstSDPMessage *copy; + gst_sdp_message_copy (ret, ©); + webrtc->priv->last_generated_answer = + gst_webrtc_session_description_new (GST_WEBRTC_SDP_TYPE_ANSWER, copy); + } return ret; } @@ -2789,8 +3367,19 @@ gst_webrtc_bin_create_offer (GstWebRTCBin * webrtc, data->promise = gst_promise_ref (promise); data->type = GST_WEBRTC_SDP_TYPE_OFFER; - gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task, - data, (GDestroyNotify) _free_create_sdp_data); + if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task, + data, (GDestroyNotify) _free_create_sdp_data, promise)) { + GError *error = + g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED, + "Could not create offer. webrtcbin is closed"); + GstStructure *s = + gst_structure_new ("application/x-gstwebrtcbin-promise-error", + "error", G_TYPE_ERROR, error, NULL); + + gst_promise_reply (promise, s); + + g_clear_error (&error); + } } static void @@ -2804,8 +3393,19 @@ gst_webrtc_bin_create_answer (GstWebRTCBin * webrtc, data->promise = gst_promise_ref (promise); data->type = GST_WEBRTC_SDP_TYPE_ANSWER; - gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task, - data, (GDestroyNotify) _free_create_sdp_data); + if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _create_sdp_task, + data, (GDestroyNotify) _free_create_sdp_data, promise)) { + GError *error = + g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED, + "Could not create answer. webrtcbin is closed."); + GstStructure *s = + gst_structure_new ("application/x-gstwebrtcbin-promise-error", + "error", G_TYPE_ERROR, error, NULL); + + gst_promise_reply (promise, s); + + g_clear_error (&error); + } } static GstWebRTCBinPad * @@ -2951,7 +3551,14 @@ _connect_output_stream (GstWebRTCBin * webrtc, */ gchar *pad_name; - GST_INFO_OBJECT (webrtc, "linking output stream %u", session_id); + if (stream->output_connected) { + GST_DEBUG_OBJECT (webrtc, "stream %" GST_PTR_FORMAT " is already " + "connected to rtpbin. Not connecting", stream); + return; + } + + GST_INFO_OBJECT (webrtc, "linking output stream %u %" GST_PTR_FORMAT, + session_id, stream); pad_name = g_strdup_printf ("recv_rtp_sink_%u", session_id); if (!gst_element_link_pads (GST_ELEMENT (stream->receive_bin), @@ -2963,6 +3570,8 @@ _connect_output_stream (GstWebRTCBin * webrtc, /* The webrtcbin src_%u output pads will be created when rtpbin receives * data on that stream in on_rtpbin_pad_added() */ + + stream->output_connected = TRUE; } typedef struct @@ -2972,20 +3581,32 @@ typedef struct } IceCandidateItem; static void -_clear_ice_candidate_item (IceCandidateItem ** item) +_clear_ice_candidate_item (IceCandidateItem * item) { - g_free ((*item)->candidate); - g_free (*item); + g_free (item->candidate); } static void -_add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item) +_add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item, + gboolean drop_invalid) { GstWebRTCICEStream *stream; stream = _find_ice_stream_for_session (webrtc, item->mlineindex); if (stream == NULL) { - GST_WARNING_OBJECT (webrtc, "Unknown mline %u, ignoring", item->mlineindex); + if (drop_invalid) { + GST_WARNING_OBJECT (webrtc, "Unknown mline %u, dropping", + item->mlineindex); + } else { + IceCandidateItem new; + new.mlineindex = item->mlineindex; + new.candidate = g_strdup (item->candidate); + GST_INFO_OBJECT (webrtc, "Unknown mline %u, deferring", item->mlineindex); + + ICE_LOCK (webrtc); + g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new); + ICE_UNLOCK (webrtc); + } return; } @@ -2995,6 +3616,61 @@ _add_ice_candidate (GstWebRTCBin * webrtc, IceCandidateItem * item) gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, item->candidate); } +static void +_add_ice_candidates_from_sdp (GstWebRTCBin * webrtc, gint mlineindex, + const GstSDPMedia * media) +{ + gint a; + GstWebRTCICEStream *stream = NULL; + + for (a = 0; a < gst_sdp_media_attributes_len (media); a++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, a); + if (g_strcmp0 (attr->key, "candidate") == 0) { + gchar *candidate; + + if (stream == NULL) + stream = _find_ice_stream_for_session (webrtc, mlineindex); + if (stream == NULL) { + GST_WARNING_OBJECT (webrtc, + "Unknown mline %u, dropping ICE candidates from SDP", mlineindex); + return; + } + + candidate = g_strdup_printf ("a=candidate:%s", attr->value); + GST_LOG_OBJECT (webrtc, "adding ICE candidate with mline:%u, %s", + mlineindex, candidate); + gst_webrtc_ice_add_candidate (webrtc->priv->ice, stream, candidate); + g_free (candidate); + } + } +} + +static void +_add_ice_candidate_to_sdp (GstWebRTCBin * webrtc, + GstSDPMessage * sdp, gint mline_index, const gchar * candidate) +{ + GstSDPMedia *media = NULL; + + if (mline_index < sdp->medias->len) { + media = &g_array_index (sdp->medias, GstSDPMedia, mline_index); + } + + if (media == NULL) { + GST_WARNING_OBJECT (webrtc, "Couldn't find mline %d to merge ICE candidate", + mline_index); + return; + } + // Add the candidate as an attribute, first stripping off the existing + // candidate: key from the string description + if (strlen (candidate) < 10) { + GST_WARNING_OBJECT (webrtc, + "Dropping invalid ICE candidate for mline %d: %s", mline_index, + candidate); + return; + } + gst_sdp_media_add_attribute (media, "candidate", candidate + 10); +} + static gboolean _filter_sdp_fields (GQuark field_id, const GValue * value, GstStructure * new_structure) @@ -3005,6 +3681,40 @@ _filter_sdp_fields (GQuark field_id, const GValue * value, return TRUE; } +static void +_set_rtx_ptmap_from_stream (GstWebRTCBin * webrtc, TransportStream * stream) +{ + gint *rtx_pt; + gsize rtx_count; + + rtx_pt = transport_stream_get_all_pt (stream, "RTX", &rtx_count); + GST_LOG_OBJECT (stream, "have %" G_GSIZE_FORMAT " rtx payloads", rtx_count); + if (rtx_pt) { + GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map"); + gsize i; + + for (i = 0; i < rtx_count; i++) { + GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt[i]); + const GstStructure *s = gst_caps_get_structure (rtx_caps, 0); + const gchar *apt = gst_structure_get_string (s, "apt"); + + GST_LOG_OBJECT (stream, "setting rtx mapping: %s -> %u", apt, rtx_pt[i]); + gst_structure_set (pt_map, apt, G_TYPE_UINT, rtx_pt[i], NULL); + } + + GST_DEBUG_OBJECT (stream, "setting payload map on %" GST_PTR_FORMAT " : %" + GST_PTR_FORMAT " and %" GST_PTR_FORMAT, stream->rtxreceive, + stream->rtxsend, pt_map); + + if (stream->rtxreceive) + g_object_set (stream->rtxreceive, "payload-type-map", pt_map, NULL); + if (stream->rtxsend) + g_object_set (stream->rtxsend, "payload-type-map", pt_map, NULL); + + gst_structure_free (pt_map); + } +} + static void _update_transport_ptmap_from_media (GstWebRTCBin * webrtc, TransportStream * stream, const GstSDPMessage * sdp, guint media_idx) @@ -3078,7 +3788,7 @@ static void _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, const GstSDPMessage * sdp, guint media_idx, TransportStream * stream, GstWebRTCRTPTransceiver * rtp_trans, - GStrv bundled, guint bundle_idx, gboolean * should_connect_bundle_stream) + GStrv bundled, guint bundle_idx) { WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans); GstWebRTCRTPTransceiverDirection prev_dir = rtp_trans->current_direction; @@ -3086,6 +3796,7 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, const GstSDPMedia *media = gst_sdp_message_get_media (sdp, media_idx); GstWebRTCDTLSSetup new_setup; gboolean new_rtcp_mux, new_rtcp_rsize; + ReceiveState receive_state = RECEIVE_STATE_UNSET; int i; rtp_trans->mline = media_idx; @@ -3125,6 +3836,7 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, return; if (prev_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE + && new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE && prev_dir != new_dir) { GST_FIXME_OBJECT (webrtc, "implement transceiver direction changes"); return; @@ -3150,10 +3862,59 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, } } + if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) { + if (!bundled) { + /* Not a bundled stream means this entire transport is inactive, + * so set the receive state to BLOCK below */ + stream->active = FALSE; + receive_state = RECEIVE_STATE_BLOCK; + } + } else { + /* If this transceiver is active for sending or receiving, + * we still need receive at least RTCP, so need to unblock + * the receive bin below. */ + GST_LOG_OBJECT (webrtc, "marking stream %p as active", stream); + receive_state = RECEIVE_STATE_PASS; + stream->active = TRUE; + } + if (new_dir != prev_dir) { - ReceiveState receive_state = 0; + gchar *prev_dir_s, *new_dir_s; + + prev_dir_s = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + prev_dir); + new_dir_s = + _enum_value_to_string (GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + new_dir); + + GST_DEBUG_OBJECT (webrtc, "transceiver %" GST_PTR_FORMAT + " direction change from %s to %s", rtp_trans, prev_dir_s, new_dir_s); + + g_free (prev_dir_s); + prev_dir_s = NULL; + g_free (new_dir_s); + new_dir_s = NULL; + + if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE) { + GstWebRTCBinPad *pad; + + pad = _find_pad_for_mline (webrtc, GST_PAD_SRC, media_idx); + if (pad) { + GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad)); + if (target) { + GstPad *peer = gst_pad_get_peer (target); + if (peer) { + gst_pad_send_event (peer, gst_event_new_eos ()); + gst_object_unref (peer); + } + gst_object_unref (target); + } + gst_object_unref (pad); + } - GST_TRACE_OBJECT (webrtc, "transceiver direction change"); + /* XXX: send eos event up the sink pad as well? */ + } if (new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY || new_dir == GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) { @@ -3199,35 +3960,35 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc, webrtc_transceiver_set_transport (trans, item); } - if (!bundled) - _connect_output_stream (webrtc, trans->stream, media_idx); - else - *should_connect_bundle_stream = TRUE; + _connect_output_stream (webrtc, trans->stream, + bundled ? bundle_idx : media_idx); /* delay adding the pad until rtpbin creates the recv output pad * to ghost to so queries/events travel through the pipeline correctly * as soon as the pad is added */ _add_pad_to_list (webrtc, pad); } - receive_state = RECEIVE_STATE_PASS; - } else if (!bundled) { - receive_state = RECEIVE_STATE_DROP; } - if (!bundled || bundle_idx == media_idx) - g_object_set (stream, "dtls-client", - new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL); - - /* Must be after setting the "dtls-client" so that data is not pushed into - * the dtlssrtp elements before the ssl direction has been set which will - * throw SSL errors */ - if (receive_state > 0) - transport_receive_bin_set_receive_state (stream->receive_bin, - receive_state); - rtp_trans->mline = media_idx; rtp_trans->current_direction = new_dir; } + + if (!bundled || bundle_idx == media_idx) { + if (stream->rtxsend || stream->rtxreceive) { + _set_rtx_ptmap_from_stream (webrtc, stream); + } + + g_object_set (stream, "dtls-client", + new_setup == GST_WEBRTC_DTLS_SETUP_ACTIVE, NULL); + } + + /* Must be after setting the "dtls-client" so that data is not pushed into + * the dtlssrtp elements before the ssl direction has been set which will + * throw SSL errors */ + if (receive_state != RECEIVE_STATE_UNSET) + transport_receive_bin_set_receive_state (stream->receive_bin, + receive_state); } /* must be called with the pc lock held */ @@ -3250,7 +4011,7 @@ _generate_data_channel_id (GstWebRTCBin * webrtc) /* TODO: a better search algorithm */ do { - GstWebRTCDataChannel *channel; + WebRTCDataChannel *channel; new_id++; @@ -3317,30 +4078,44 @@ _update_data_channel_from_sdp_media (GstWebRTCBin * webrtc, webrtc->priv->sctp_transport->max_message_size = max_size; - g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port", - local_port, NULL); - g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port", - remote_port, NULL); + { + guint orig_local_port, orig_remote_port; + + /* XXX: sctpassociation warns if we are in the wrong state */ + g_object_get (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port", + &orig_local_port, NULL); + + if (orig_local_port != local_port) + g_object_set (webrtc->priv->sctp_transport->sctpdec, "local-sctp-port", + local_port, NULL); + + g_object_get (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port", + &orig_remote_port, NULL); + if (orig_remote_port != remote_port) + g_object_set (webrtc->priv->sctp_transport->sctpenc, "remote-sctp-port", + remote_port, NULL); + } for (i = 0; i < webrtc->priv->data_channels->len; i++) { - GstWebRTCDataChannel *channel; + WebRTCDataChannel *channel; - channel = - g_array_index (webrtc->priv->data_channels, GstWebRTCDataChannel *, i); + channel = g_ptr_array_index (webrtc->priv->data_channels, i); - if (channel->id == -1) - channel->id = _generate_data_channel_id (webrtc); - if (channel->id == -1) + if (channel->parent.id == -1) + channel->parent.id = _generate_data_channel_id (webrtc); + if (channel->parent.id == -1) GST_ELEMENT_WARNING (webrtc, RESOURCE, NOT_FOUND, ("%s", "Failed to generate an identifier for a data channel"), NULL); if (webrtc->priv->sctp_transport->association_established - && !channel->negotiated && !channel->opened) { - _link_data_channel_to_sctp (webrtc, channel); - gst_webrtc_data_channel_start_negotiation (channel); + && !channel->parent.negotiated && !channel->opened) { + webrtc_data_channel_link_to_sctp (channel, webrtc->priv->sctp_transport); + webrtc_data_channel_start_negotiation (channel); } } + stream->active = TRUE; + receive = TRANSPORT_RECEIVE_BIN (stream->receive_bin); transport_receive_bin_set_receive_state (receive, RECEIVE_STATE_PASS); } @@ -3353,6 +4128,8 @@ _find_compatible_unassociated_transceiver (GstWebRTCRTPTransceiver * p1, return FALSE; if (p1->mline != -1) return FALSE; + if (p1->stopped) + return FALSE; return TRUE; } @@ -3408,11 +4185,13 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, gboolean ret = FALSE; GStrv bundled = NULL; guint bundle_idx = 0; - gboolean should_connect_bundle_stream = FALSE; TransportStream *bundle_stream = NULL; - if (!_parse_bundle (sdp->sdp, &bundled)) - goto done; + /* FIXME: With some peers, it's possible we could have + * multiple bundles to deal with, although I've never seen one yet */ + if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) + if (!_parse_bundle (sdp->sdp, &bundled)) + goto done; if (bundled) { @@ -3424,13 +4203,19 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, bundle_stream = _get_or_create_transport_stream (webrtc, bundle_idx, _message_media_is_datachannel (sdp->sdp, bundle_idx)); - - _connect_rtpfunnel (webrtc, bundle_idx); + /* Mark the bundle stream as inactive to start. It will be set to TRUE + * by any bundled mline that is active, and at the end we set the + * receivebin to BLOCK if all mlines were inactive. */ + bundle_stream->active = FALSE; g_array_set_size (bundle_stream->ptmap, 0); for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) { + /* When bundling, we need to do this up front, or else RTX + * parameters aren't set up properly for the bundled streams */ _update_transport_ptmap_from_media (webrtc, bundle_stream, sdp->sdp, i); } + + _connect_rtpfunnel (webrtc, bundle_idx); } for (i = 0; i < gst_sdp_message_medias_len (sdp->sdp); i++) { @@ -3453,6 +4238,8 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, stream = _get_or_create_transport_stream (webrtc, transport_idx, _message_media_is_datachannel (sdp->sdp, transport_idx)); if (!bundled) { + /* When bundling, these were all set up above, but when not + * bundling we need to do it now */ g_array_set_size (stream->ptmap, 0); _update_transport_ptmap_from_media (webrtc, stream, sdp->sdp, i); } @@ -3466,24 +4253,25 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, } else { if (g_strcmp0 (gst_sdp_media_get_media (media), "audio") == 0 || g_strcmp0 (gst_sdp_media_get_media (media), "video") == 0) { - if (trans) { - _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream, - trans, bundled, bundle_idx, &should_connect_bundle_stream); - } else { + /* No existing transceiver, find an unused one */ + if (!trans) { trans = _find_transceiver (webrtc, NULL, (FindTransceiverFunc) _find_compatible_unassociated_transceiver); - /* XXX: default to the advertised direction in the sdp for new - * transceviers. The spec doesn't actually say what happens here, only - * that calls to setDirection will change the value. Nothing about - * a default value when the transceiver is created internally */ - if (!trans) { - trans = - GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc, - _get_direction_from_media (media), i)); - } - _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream, - trans, bundled, bundle_idx, &should_connect_bundle_stream); } + + /* Still no transceiver? Create one */ + /* XXX: default to the advertised direction in the sdp for new + * transceivers. The spec doesn't actually say what happens here, only + * that calls to setDirection will change the value. Nothing about + * a default value when the transceiver is created internally */ + if (!trans) { + trans = + GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc, + _get_direction_from_media (media), i)); + } + + _update_transceiver_from_sdp_media (webrtc, sdp->sdp, i, stream, + trans, bundled, bundle_idx); } else if (_message_media_is_datachannel (sdp->sdp, i)) { _update_data_channel_from_sdp_media (webrtc, sdp->sdp, i, stream); } else { @@ -3492,16 +4280,20 @@ _update_transceivers_from_sdp (GstWebRTCBin * webrtc, SDPSource source, } } - if (should_connect_bundle_stream) { - g_assert (bundle_stream); - _connect_output_stream (webrtc, bundle_stream, bundle_idx); + if (bundle_stream && bundle_stream->active == FALSE) { + /* No bundled mline marked the bundle as active, so block the receive bin, as + * this bundle is completely inactive */ + GST_LOG_OBJECT (webrtc, + "All mlines in bundle %u are inactive. Blocking receiver", bundle_idx); + transport_receive_bin_set_receive_state (bundle_stream->receive_bin, + RECEIVE_STATE_BLOCK); } ret = TRUE; done: - if (bundled) - g_strfreev (bundled); + g_strfreev (bundled); + return ret; } @@ -3517,6 +4309,7 @@ static void _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) { GstWebRTCSignalingState new_signaling_state = webrtc->signaling_state; + gboolean signalling_state_changed = FALSE; GError *error = NULL; GStrv bundled = NULL; guint bundle_idx = 0; @@ -3530,7 +4323,7 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) gchar *sdp_text = gst_sdp_message_as_text (sd->sdp->sdp); GST_INFO_OBJECT (webrtc, "Attempting to set %s %s in the %s state", _sdp_source_to_string (sd->source), type_str, state); - GST_INFO_OBJECT (webrtc, "SDP contents\n%s", sdp_text); + GST_TRACE_OBJECT (webrtc, "SDP contents\n%s", sdp_text); g_free (sdp_text); g_free (state); g_free (type_str); @@ -3547,8 +4340,9 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) goto out; } - if (!_parse_bundle (sd->sdp->sdp, &bundled)) - goto out; + if (webrtc->bundle_policy != GST_WEBRTC_BUNDLE_POLICY_NONE) + if (!_parse_bundle (sd->sdp->sdp, &bundled)) + goto out; if (bundled) { if (!_get_bundle_index (sd->sdp->sdp, bundled, &bundle_idx)) { @@ -3656,25 +4450,9 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) } } - if (new_signaling_state != webrtc->signaling_state) { - gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE, - webrtc->signaling_state); - gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE, - new_signaling_state); - GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s " - "to %s", from, to); - webrtc->signaling_state = new_signaling_state; - PC_UNLOCK (webrtc); - g_object_notify (G_OBJECT (webrtc), "signaling-state"); - PC_LOCK (webrtc); - - g_free (from); - g_free (to); - } - if (sd->sdp->type == GST_WEBRTC_SDP_TYPE_ROLLBACK) { /* FIXME: - * If the mid value of an RTCRtpTransceiver was set to a non-null value + * If the mid value of an RTCRtpTransceiver was set to a non-null value * by the RTCSessionDescription that is being rolled back, set the mid * value of that transceiver to null, as described by [JSEP] * (section 4.1.7.2.). @@ -3688,44 +4466,98 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) */ } - if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) { - gboolean prev_need_negotiation = webrtc->priv->need_negotiation; + if (webrtc->signaling_state != new_signaling_state) { + webrtc->signaling_state = new_signaling_state; + signalling_state_changed = TRUE; + } + + { + gboolean ice_controller = FALSE; + + /* get the current value so we don't change ice controller from TRUE to + * FALSE on renegotiation or once set to TRUE for the initial local offer */ + ice_controller = gst_webrtc_ice_get_is_controller (webrtc->priv->ice); + + /* we control ice negotiation if we send the initial offer */ + ice_controller |= + new_signaling_state == GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER + && webrtc->current_remote_description == NULL; + /* or, if the remote is an ice-lite peer */ + ice_controller |= new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE + && webrtc->current_remote_description + && _message_has_attribute_key (webrtc->current_remote_description->sdp, + "ice-lite"); + + GST_DEBUG_OBJECT (webrtc, "we are in ice controlling mode: %s", + ice_controller ? "true" : "false"); + gst_webrtc_ice_set_is_controller (webrtc->priv->ice, ice_controller); + } + + if (new_signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) { GList *tmp; /* media modifications */ _update_transceivers_from_sdp (webrtc, sd->source, sd->sdp); - for (tmp = webrtc->priv->pending_sink_transceivers; tmp; tmp = tmp->next) { + for (tmp = webrtc->priv->pending_sink_transceivers; tmp;) { GstWebRTCBinPad *pad = GST_WEBRTC_BIN_PAD (tmp->data); + GstWebRTCRTPTransceiverDirection new_dir; + GList *old = tmp; const GstSDPMedia *media; + if (!pad->received_caps) { + GST_LOG_OBJECT (pad, "has not received any caps yet. Skipping."); + tmp = tmp->next; + continue; + } + + if (pad->mlineindex >= gst_sdp_message_medias_len (sd->sdp->sdp)) { + GST_DEBUG_OBJECT (pad, "not mentioned in this description. Skipping"); + tmp = tmp->next; + continue; + } + media = gst_sdp_message_get_media (sd->sdp->sdp, pad->mlineindex); /* skip rejected media */ - /* FIXME: arrange for an appropriate flow return */ - if (gst_sdp_media_get_port (media) == 0) + if (gst_sdp_media_get_port (media) == 0) { + /* FIXME: arrange for an appropriate flow return */ + GST_FIXME_OBJECT (pad, "Media has been rejected. Need to arrange for " + "a more correct flow return."); + tmp = tmp->next; + continue; + } + + if (!pad->trans) { + GST_LOG_OBJECT (pad, "doesn't have a transceiver"); + tmp = tmp->next; + continue; + } + + new_dir = pad->trans->direction; + if (new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY && + new_dir != GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV) { + GST_LOG_OBJECT (pad, "transceiver %" GST_PTR_FORMAT " is not sending " + "data at the moment. Not connecting input stream yet", pad->trans); + tmp = tmp->next; continue; + } + GST_LOG_OBJECT (pad, "Connecting input stream to rtpbin with " + "transceiver %" GST_PTR_FORMAT " and caps %" GST_PTR_FORMAT, + pad->trans, pad->received_caps); _connect_input_stream (webrtc, pad); gst_pad_remove_probe (GST_PAD (pad), pad->block_id); pad->block_id = 0; - } - - g_list_free_full (webrtc->priv->pending_sink_transceivers, - (GDestroyNotify) gst_object_unref); - webrtc->priv->pending_sink_transceivers = NULL; - /* If connection's signaling state is now stable, update the - * negotiation-needed flag. If connection's [[ needNegotiation]] slot - * was true both before and after this update, queue a task to check - * connection's [[needNegotiation]] slot and, if still true, fire a - * simple event named negotiationneeded at connection.*/ - _update_need_negotiation (webrtc); - if (prev_need_negotiation && webrtc->priv->need_negotiation) { - _check_need_negotiation_task (webrtc, NULL); + tmp = tmp->next; + gst_object_unref (old->data); + webrtc->priv->pending_sink_transceivers = + g_list_delete_link (webrtc->priv->pending_sink_transceivers, old); } } for (i = 0; i < gst_sdp_message_medias_len (sd->sdp->sdp); i++) { + const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i); gchar *ufrag, *pwd; TransportStream *item; @@ -3734,7 +4566,6 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) _message_media_is_datachannel (sd->sdp->sdp, bundled ? bundle_idx : i)); if (sd->source == SDP_REMOTE) { - const GstSDPMedia *media = gst_sdp_message_get_media (sd->sdp->sdp, i); guint j; for (j = 0; j < gst_sdp_media_attributes_len (media); j++) { @@ -3757,44 +4588,93 @@ _set_description_task (GstWebRTCBin * webrtc, struct set_description *sd) } } - if (bundled && bundle_idx != i) - continue; - - _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd); + if (sd->source == SDP_LOCAL && (!bundled || bundle_idx == i)) { + _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd); - if (sd->source == SDP_LOCAL) gst_webrtc_ice_set_local_credentials (webrtc->priv->ice, item->stream, ufrag, pwd); - else + g_free (ufrag); + g_free (pwd); + } else if (sd->source == SDP_REMOTE && !_media_is_bundle_only (media)) { + _get_ice_credentials_from_sdp_media (sd->sdp->sdp, i, &ufrag, &pwd); + gst_webrtc_ice_set_remote_credentials (webrtc->priv->ice, item->stream, ufrag, pwd); - g_free (ufrag); - g_free (pwd); + g_free (ufrag); + g_free (pwd); + } } - for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) { - IceStreamItem *item = - &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i); + if (sd->source == SDP_LOCAL) { + for (i = 0; i < webrtc->priv->ice_stream_map->len; i++) { + IceStreamItem *item = + &g_array_index (webrtc->priv->ice_stream_map, IceStreamItem, i); - gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream); + gst_webrtc_ice_gather_candidates (webrtc->priv->ice, item->stream); + } } + /* Add any pending trickle ICE candidates if we have both offer and answer */ if (webrtc->current_local_description && webrtc->current_remote_description) { int i; - for (i = 0; i < webrtc->priv->pending_ice_candidates->len; i++) { + GstWebRTCSessionDescription *remote_sdp = + webrtc->current_remote_description; + + /* Add any remote ICE candidates from the remote description to + * support non-trickle peers first */ + for (i = 0; i < gst_sdp_message_medias_len (remote_sdp->sdp); i++) { + const GstSDPMedia *media = gst_sdp_message_get_media (remote_sdp->sdp, i); + _add_ice_candidates_from_sdp (webrtc, i, media); + } + + ICE_LOCK (webrtc); + for (i = 0; i < webrtc->priv->pending_remote_ice_candidates->len; i++) { IceCandidateItem *item = - g_array_index (webrtc->priv->pending_ice_candidates, - IceCandidateItem *, i); + &g_array_index (webrtc->priv->pending_remote_ice_candidates, + IceCandidateItem, i); + + _add_ice_candidate (webrtc, item, TRUE); + } + g_array_set_size (webrtc->priv->pending_remote_ice_candidates, 0); + ICE_UNLOCK (webrtc); + } + + /* + * If connection's signaling state changed above, fire an event named + * signalingstatechange at connection. + */ + if (signalling_state_changed) { + gchar *from = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE, + webrtc->signaling_state); + gchar *to = _enum_value_to_string (GST_TYPE_WEBRTC_SIGNALING_STATE, + new_signaling_state); + GST_TRACE_OBJECT (webrtc, "notify signaling-state from %s " + "to %s", from, to); + PC_UNLOCK (webrtc); + g_object_notify (G_OBJECT (webrtc), "signaling-state"); + PC_LOCK (webrtc); + + g_free (from); + g_free (to); + } + + if (webrtc->signaling_state == GST_WEBRTC_SIGNALING_STATE_STABLE) { + gboolean prev_need_negotiation = webrtc->priv->need_negotiation; - _add_ice_candidate (webrtc, item); + /* If connection's signaling state is now stable, update the + * negotiation-needed flag. If connection's [[ needNegotiation]] slot + * was true both before and after this update, queue a task to check + * connection's [[needNegotiation]] slot and, if still true, fire a + * simple event named negotiationneeded at connection.*/ + _update_need_negotiation (webrtc); + if (prev_need_negotiation && webrtc->priv->need_negotiation) { + _check_need_negotiation_task (webrtc, NULL); } - g_array_set_size (webrtc->priv->pending_ice_candidates, 0); } out: - if (bundled) - g_strfreev (bundled); + g_strfreev (bundled); PC_UNLOCK (webrtc); gst_promise_reply (sd->promise, NULL); @@ -3828,8 +4708,20 @@ gst_webrtc_bin_set_remote_description (GstWebRTCBin * webrtc, sd->source = SDP_REMOTE; sd->sdp = gst_webrtc_session_description_copy (remote_sdp); - gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task, - sd, (GDestroyNotify) _free_set_description_data); + if (!gst_webrtc_bin_enqueue_task (webrtc, + (GstWebRTCBinFunc) _set_description_task, sd, + (GDestroyNotify) _free_set_description_data, promise)) { + GError *error = + g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED, + "Could not set remote description. webrtcbin is closed."); + GstStructure *s = + gst_structure_new ("application/x-gstwebrtcbin-promise-error", + "error", G_TYPE_ERROR, error, NULL); + + gst_promise_reply (promise, s); + + g_clear_error (&error); + } return; @@ -3857,8 +4749,20 @@ gst_webrtc_bin_set_local_description (GstWebRTCBin * webrtc, sd->source = SDP_LOCAL; sd->sdp = gst_webrtc_session_description_copy (local_sdp); - gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _set_description_task, - sd, (GDestroyNotify) _free_set_description_data); + if (!gst_webrtc_bin_enqueue_task (webrtc, + (GstWebRTCBinFunc) _set_description_task, sd, + (GDestroyNotify) _free_set_description_data, promise)) { + GError *error = + g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED, + "Could not set remote description. webrtcbin is closed"); + GstStructure *s = + gst_structure_new ("application/x-gstwebrtcbin-promise-error", + "error", G_TYPE_ERROR, error, NULL); + + gst_promise_reply (promise, s); + + g_clear_error (&error); + } return; @@ -3873,20 +4777,23 @@ static void _add_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item) { if (!webrtc->current_local_description || !webrtc->current_remote_description) { - IceCandidateItem *new = g_new0 (IceCandidateItem, 1); - new->mlineindex = item->mlineindex; - new->candidate = g_strdup (item->candidate); + IceCandidateItem new; + new.mlineindex = item->mlineindex; + new.candidate = g_steal_pointer (&item->candidate); - g_array_append_val (webrtc->priv->pending_ice_candidates, new); + ICE_LOCK (webrtc); + g_array_append_val (webrtc->priv->pending_remote_ice_candidates, new); + ICE_UNLOCK (webrtc); } else { - _add_ice_candidate (webrtc, item); + _add_ice_candidate (webrtc, item, FALSE); } } static void _free_ice_candidate_item (IceCandidateItem * item) { - _clear_ice_candidate_item (&item); + _clear_ice_candidate_item (item); + g_free (item); } static void @@ -3903,40 +4810,92 @@ gst_webrtc_bin_add_ice_candidate (GstWebRTCBin * webrtc, guint mline, item->candidate = g_strdup_printf ("a=%s", attr); gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _add_ice_candidate_task, item, - (GDestroyNotify) _free_ice_candidate_item); + (GDestroyNotify) _free_ice_candidate_item, NULL); } static void -_on_ice_candidate_task (GstWebRTCBin * webrtc, IceCandidateItem * item) -{ - const gchar *cand = item->candidate; +_on_local_ice_candidate_task (GstWebRTCBin * webrtc) +{ + gsize i; + GArray *items; + + ICE_LOCK (webrtc); + if (webrtc->priv->pending_local_ice_candidates->len == 0) { + ICE_UNLOCK (webrtc); + GST_LOG_OBJECT (webrtc, "No ICE candidates to process right now"); + return; /* Nothing to process */ + } + /* Take the array so we can process it all and free it later + * without holding the lock + * FIXME: When we depend on GLib 2.64, we can use g_array_steal() + * here */ + items = webrtc->priv->pending_local_ice_candidates; + /* Replace with a new array */ + webrtc->priv->pending_local_ice_candidates = + g_array_new (FALSE, TRUE, sizeof (IceCandidateItem)); + g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates, + (GDestroyNotify) _clear_ice_candidate_item); + ICE_UNLOCK (webrtc); - if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) { - /* stripping away "a=" */ - cand += 2; - } + for (i = 0; i < items->len; i++) { + IceCandidateItem *item = &g_array_index (items, IceCandidateItem, i); + const gchar *cand = item->candidate; + + if (!g_ascii_strncasecmp (cand, "a=candidate:", 12)) { + /* stripping away "a=" */ + cand += 2; + } - GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s", - item->mlineindex, cand); + GST_TRACE_OBJECT (webrtc, "produced ICE candidate for mline:%u and %s", + item->mlineindex, cand); - PC_UNLOCK (webrtc); - g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL], - 0, item->mlineindex, cand); - PC_LOCK (webrtc); + /* First, merge this ice candidate into the appropriate mline + * in the local-description SDP. + * Second, emit the on-ice-candidate signal for the app. + * + * FIXME: This ICE candidate should be stored somewhere with + * the associated mid and also merged back into any subsequent + * local descriptions on renegotiation */ + if (webrtc->current_local_description) + _add_ice_candidate_to_sdp (webrtc, webrtc->current_local_description->sdp, + item->mlineindex, cand); + if (webrtc->pending_local_description) + _add_ice_candidate_to_sdp (webrtc, webrtc->pending_local_description->sdp, + item->mlineindex, cand); + + PC_UNLOCK (webrtc); + g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL], + 0, item->mlineindex, cand); + PC_LOCK (webrtc); + + } + g_array_free (items, TRUE); } static void -_on_ice_candidate (GstWebRTCICE * ice, guint session_id, +_on_local_ice_candidate_cb (GstWebRTCICE * ice, guint session_id, gchar * candidate, GstWebRTCBin * webrtc) { - IceCandidateItem *item = g_new0 (IceCandidateItem, 1); + IceCandidateItem item; + gboolean queue_task = FALSE; - item->mlineindex = session_id; - item->candidate = g_strdup (candidate); + item.mlineindex = session_id; + item.candidate = g_strdup (candidate); - gst_webrtc_bin_enqueue_task (webrtc, - (GstWebRTCBinFunc) _on_ice_candidate_task, item, - (GDestroyNotify) _free_ice_candidate_item); + ICE_LOCK (webrtc); + g_array_append_val (webrtc->priv->pending_local_ice_candidates, item); + + /* Let the first pending candidate queue a task each time, which will + * handle any that arrive between now and when the task runs */ + if (webrtc->priv->pending_local_ice_candidates->len == 1) + queue_task = TRUE; + ICE_UNLOCK (webrtc); + + if (queue_task) { + GST_TRACE_OBJECT (webrtc, "Queueing on_ice_candidate_task"); + gst_webrtc_bin_enqueue_task (webrtc, + (GstWebRTCBinFunc) _on_local_ice_candidate_task, NULL, NULL, NULL); + } } /* https://www.w3.org/TR/webrtc/#dfn-stats-selection-algorithm */ @@ -4005,8 +4964,18 @@ gst_webrtc_bin_get_stats (GstWebRTCBin * webrtc, GstPad * pad, if (pad) stats->pad = gst_object_ref (pad); - gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task, - stats, (GDestroyNotify) _free_get_stats); + if (!gst_webrtc_bin_enqueue_task (webrtc, (GstWebRTCBinFunc) _get_stats_task, + stats, (GDestroyNotify) _free_get_stats, promise)) { + GError *error = + g_error_new (GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_CLOSED, + "Could not retrieve statistics. webrtcbin is closed."); + GstStructure *s = gst_structure_new ("application/x-gst-promise-error", + "error", G_TYPE_ERROR, error, NULL); + + gst_promise_reply (promise, s); + + g_clear_error (&error); + } } static GstWebRTCRTPTransceiver * @@ -4020,6 +4989,9 @@ gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc, NULL); trans = _create_webrtc_transceiver (webrtc, direction, -1); + GST_LOG_OBJECT (webrtc, + "Created new unassociated transceiver %" GST_PTR_FORMAT, trans); + rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans); if (caps) rtp_trans->codec_preferences = gst_caps_ref (caps); @@ -4030,22 +5002,20 @@ gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc, static void _deref_and_unref (GstObject ** object) { - if (object) - gst_object_unref (*object); + gst_clear_object (object); } static GArray * gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc) { - GArray *arr = g_array_new (FALSE, TRUE, sizeof (gpointer)); + GArray *arr = g_array_new (FALSE, TRUE, sizeof (GstWebRTCRTPTransceiver *)); int i; g_array_set_clear_func (arr, (GDestroyNotify) _deref_and_unref); for (i = 0; i < webrtc->priv->transceivers->len; i++) { GstWebRTCRTPTransceiver *trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - i); + g_ptr_array_index (webrtc->priv->transceivers, i); gst_object_ref (trans); g_array_append_val (arr, trans); } @@ -4063,9 +5033,7 @@ gst_webrtc_bin_get_transceiver (GstWebRTCBin * webrtc, guint idx) goto done; } - trans = - g_array_index (webrtc->priv->transceivers, GstWebRTCRTPTransceiver *, - idx); + trans = g_ptr_array_index (webrtc->priv->transceivers, idx); gst_object_ref (trans); done: @@ -4094,7 +5062,7 @@ copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data) return TRUE; } -static GstWebRTCDataChannel * +static WebRTCDataChannel * gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label, GstStructure * init_params) { @@ -4105,7 +5073,7 @@ gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label, gboolean negotiated; gint id; GstWebRTCPriorityType priority; - GstWebRTCDataChannel *ret; + WebRTCDataChannel *ret; gint max_channels = 65534; g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), NULL); @@ -4172,7 +5140,7 @@ gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label, PC_LOCK (webrtc); /* check if the id has been used already */ if (id != -1) { - GstWebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id); + WebRTCDataChannel *channel = _find_data_channel_for_id (webrtc, id); if (channel) { GST_ELEMENT_WARNING (webrtc, LIBRARY, SETTINGS, ("Attempting to add a data channel with a duplicate ID: %i", id), @@ -4194,7 +5162,7 @@ gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label, } } - ret = g_object_new (GST_TYPE_WEBRTC_DATA_CHANNEL, "label", label, + ret = g_object_new (WEBRTC_TYPE_DATA_CHANNEL, "label", label, "ordered", ordered, "max-packet-lifetime", max_packet_lifetime, "max-retransmits", max_retransmits, "protocol", protocol, "negotiated", negotiated, "id", id, "priority", priority, NULL); @@ -4208,12 +5176,15 @@ gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label, ret = gst_object_ref (ret); ret->webrtcbin = webrtc; - g_array_append_val (webrtc->priv->data_channels, ret); - _link_data_channel_to_sctp (webrtc, ret); + g_ptr_array_add (webrtc->priv->data_channels, ret); + webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport); if (webrtc->priv->sctp_transport && webrtc->priv->sctp_transport->association_established - && !ret->negotiated) - gst_webrtc_data_channel_start_negotiation (ret); + && !ret->parent.negotiated) { + webrtc_data_channel_start_negotiation (ret); + } else { + _update_need_negotiation (webrtc); + } } PC_UNLOCK (webrtc); @@ -4237,6 +5208,7 @@ on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad, TransportStream *stream; GstWebRTCBinPad *pad; guint media_idx = 0; + gboolean found_ssrc = FALSE; guint i; if (sscanf (new_pad_name, "recv_rtp_src_%u_%u_%u", &session_id, &ssrc, @@ -4256,10 +5228,15 @@ on_rtpbin_pad_added (GstElement * rtpbin, GstPad * new_pad, &g_array_index (stream->remote_ssrcmap, SsrcMapItem, i); if (item->ssrc == ssrc) { media_idx = item->media_idx; + found_ssrc = TRUE; break; } } + if (!found_ssrc) { + GST_WARNING_OBJECT (webrtc, "Could not find ssrc %u", ssrc); + } + rtp_trans = _find_transceiver_for_mline (webrtc, media_idx); if (!rtp_trans) g_warn_if_reached (); @@ -4320,7 +5297,8 @@ on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id, GstWebRTCBin * webrtc) { TransportStream *stream; - GstStructure *pt_map = gst_structure_new_empty ("application/x-rtp-pt-map"); + gboolean have_rtx = FALSE; + GstStructure *pt_map = NULL; GstElement *ret = NULL; GstWebRTCRTPTransceiver *trans; @@ -4328,41 +5306,28 @@ on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id, trans = _find_transceiver (webrtc, &session_id, (FindTransceiverFunc) transceiver_match_for_mline); - if (stream) { - guint i; - - for (i = 0; i < stream->ptmap->len; i++) { - PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i); - if (!gst_caps_is_empty (item->caps)) { - GstStructure *s = gst_caps_get_structure (item->caps, 0); - gint pt; - const gchar *apt_str = gst_structure_get_string (s, "apt"); - - if (!apt_str) - continue; - - if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), "RTX") && - gst_structure_get_int (s, "payload", &pt)) { - gst_structure_set (pt_map, apt_str, G_TYPE_UINT, pt, NULL); - } - } - } - } + if (stream) + have_rtx = transport_stream_get_pt (stream, "RTX") != 0; GST_LOG_OBJECT (webrtc, "requesting aux sender for stream %" GST_PTR_FORMAT " with transport %" GST_PTR_FORMAT " and pt map %" GST_PTR_FORMAT, stream, trans, pt_map); - if (gst_structure_n_fields (pt_map)) { + if (have_rtx) { GstElement *rtx; GstPad *pad; gchar *name; + if (stream->rtxsend) { + GST_WARNING_OBJECT (webrtc, "rtprtxsend already created! rtpbin bug?!"); + goto out; + } + GST_INFO ("creating AUX sender"); ret = gst_bin_new (NULL); rtx = gst_element_factory_make ("rtprtxsend", NULL); - g_object_set (rtx, "payload-type-map", pt_map, "max-size-packets", 500, - NULL); + g_object_set (rtx, "max-size-packets", 500, NULL); + _set_rtx_ptmap_from_stream (webrtc, stream); if (WEBRTC_TRANSCEIVER (trans)->local_rtx_ssrc_map) g_object_set (rtx, "ssrc-map", @@ -4381,9 +5346,13 @@ on_rtpbin_request_aux_sender (GstElement * rtpbin, guint session_id, gst_element_add_pad (ret, gst_ghost_pad_new (name, pad)); g_free (name); gst_object_unref (pad); + + stream->rtxsend = gst_object_ref (rtx); } - gst_structure_free (pt_map); +out: + if (pt_map) + gst_structure_free (pt_map); return ret; } @@ -4406,28 +5375,27 @@ on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id, rtx_pt = transport_stream_get_pt (stream, "RTX"); } - GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT - " with pt red:%u rtx:%u", stream, red_pt, rtx_pt); + GST_LOG_OBJECT (webrtc, "requesting aux receiver for stream %" GST_PTR_FORMAT, + stream); if (red_pt || rtx_pt) ret = gst_bin_new (NULL); if (rtx_pt) { - GstCaps *rtx_caps = transport_stream_get_caps_for_pt (stream, rtx_pt); - GstElement *rtx = gst_element_factory_make ("rtprtxreceive", NULL); - GstStructure *pt_map; - const GstStructure *s = gst_caps_get_structure (rtx_caps, 0); + if (stream->rtxreceive) { + GST_WARNING_OBJECT (webrtc, + "rtprtxreceive already created! rtpbin bug?!"); + goto error; + } - gst_bin_add (GST_BIN (ret), rtx); + stream->rtxreceive = gst_element_factory_make ("rtprtxreceive", NULL); + _set_rtx_ptmap_from_stream (webrtc, stream); - pt_map = gst_structure_new_empty ("application/x-rtp-pt-map"); - gst_structure_set (pt_map, gst_structure_get_string (s, "apt"), G_TYPE_UINT, - rtx_pt, NULL); - g_object_set (rtx, "payload-type-map", pt_map, NULL); + gst_bin_add (GST_BIN (ret), stream->rtxreceive); - sinkpad = gst_element_get_static_pad (rtx, "sink"); + sinkpad = gst_element_get_static_pad (stream->rtxreceive, "sink"); - prev = rtx; + prev = gst_object_ref (stream->rtxreceive); } if (red_pt) { @@ -4465,7 +5433,13 @@ on_rtpbin_request_aux_receiver (GstElement * rtpbin, guint session_id, gst_element_add_pad (ret, ghost); } +out: return ret; + +error: + if (ret) + gst_object_unref (ret); + goto out; } static GstElement * @@ -4588,10 +5562,84 @@ on_rtpbin_request_fec_encoder (GstElement * rtpbin, guint session_id, return ret; } +static void +on_rtpbin_bye_ssrc (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u received bye", session_id, ssrc); +} + +static void +on_rtpbin_bye_timeout (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u bye timeout", session_id, ssrc); +} + +static void +on_rtpbin_sender_timeout (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender timeout", session_id, + ssrc); +} + +static void +on_rtpbin_new_ssrc (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u new ssrc", session_id, ssrc); +} + static void on_rtpbin_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc, GstWebRTCBin * webrtc) { + GST_INFO_OBJECT (webrtc, "session %u ssrc %u active", session_id, ssrc); +} + +static void +on_rtpbin_ssrc_collision (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u collision", session_id, ssrc); +} + +static void +on_rtpbin_ssrc_sdes (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u sdes", session_id, ssrc); +} + +static void +on_rtpbin_ssrc_validated (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u validated", session_id, ssrc); +} + +static void +on_rtpbin_timeout (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u timeout", session_id, ssrc); +} + +static void +on_rtpbin_new_sender_ssrc (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u new sender ssrc", session_id, + ssrc); +} + +static void +on_rtpbin_sender_ssrc_active (GstElement * rtpbin, guint session_id, guint ssrc, + GstWebRTCBin * webrtc) +{ + GST_INFO_OBJECT (webrtc, "session %u ssrc %u sender ssrc active", session_id, + ssrc); } static void @@ -4616,8 +5664,13 @@ static void on_rtpbin_new_storage (GstElement * rtpbin, GstElement * storage, guint session_id, GstWebRTCBin * webrtc) { - /* TODO: when exposing latency, set size-time based on that */ - g_object_set (storage, "size-time", (guint64) 250 * GST_MSECOND, NULL); + guint64 latency = webrtc->priv->jb_latency; + + /* Add an extra 50 ms for safey */ + latency += RTPSTORAGE_EXTRA_TIME; + latency *= GST_MSECOND; + + g_object_set (storage, "size-time", latency, NULL); } static GstElement * @@ -4647,8 +5700,28 @@ _create_rtpbin (GstWebRTCBin * webrtc) G_CALLBACK (on_rtpbin_request_fec_decoder), webrtc); g_signal_connect (rtpbin, "request-fec-encoder", G_CALLBACK (on_rtpbin_request_fec_encoder), webrtc); + g_signal_connect (rtpbin, "on-bye-ssrc", + G_CALLBACK (on_rtpbin_bye_ssrc), webrtc); + g_signal_connect (rtpbin, "on-bye-timeout", + G_CALLBACK (on_rtpbin_bye_timeout), webrtc); + g_signal_connect (rtpbin, "on-new-ssrc", + G_CALLBACK (on_rtpbin_new_ssrc), webrtc); + g_signal_connect (rtpbin, "on-new-sender-ssrc", + G_CALLBACK (on_rtpbin_new_sender_ssrc), webrtc); + g_signal_connect (rtpbin, "on-sender-ssrc-active", + G_CALLBACK (on_rtpbin_sender_ssrc_active), webrtc); + g_signal_connect (rtpbin, "on-sender-timeout", + G_CALLBACK (on_rtpbin_sender_timeout), webrtc); g_signal_connect (rtpbin, "on-ssrc-active", G_CALLBACK (on_rtpbin_ssrc_active), webrtc); + g_signal_connect (rtpbin, "on-ssrc-collision", + G_CALLBACK (on_rtpbin_ssrc_collision), webrtc); + g_signal_connect (rtpbin, "on-ssrc-sdes", + G_CALLBACK (on_rtpbin_ssrc_sdes), webrtc); + g_signal_connect (rtpbin, "on-ssrc-validated", + G_CALLBACK (on_rtpbin_ssrc_validated), webrtc); + g_signal_connect (rtpbin, "on-timeout", + G_CALLBACK (on_rtpbin_timeout), webrtc); g_signal_connect (rtpbin, "new-jitterbuffer", G_CALLBACK (on_rtpbin_new_jitterbuffer), webrtc); @@ -4670,7 +5743,9 @@ gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition) if (!_have_nice_elements (webrtc) || !_have_dtls_elements (webrtc)) return GST_STATE_CHANGE_FAILURE; _start_thread (webrtc); + PC_LOCK (webrtc); _update_need_negotiation (webrtc); + PC_UNLOCK (webrtc); break; } case GST_STATE_CHANGE_READY_TO_PAUSED: @@ -4706,7 +5781,7 @@ gst_webrtc_bin_change_state (GstElement * element, GstStateChange transition) } static GstPadProbeReturn -pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused) +sink_pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused) { GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data); @@ -4742,15 +5817,21 @@ gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ, pad = _create_pad_for_sdp_media (webrtc, GST_PAD_SINK, serial); trans = _find_transceiver_for_mline (webrtc, serial); - if (!trans) + if (!trans) { trans = GST_WEBRTC_RTP_TRANSCEIVER (_create_webrtc_transceiver (webrtc, GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV, serial)); + GST_LOG_OBJECT (webrtc, "Created new transceiver %" GST_PTR_FORMAT + " for mline %u", trans, serial); + } else { + GST_LOG_OBJECT (webrtc, "Using existing transceiver %" GST_PTR_FORMAT + " for mline %u", trans, serial); + } pad->trans = gst_object_ref (trans); pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST, - (GstPadProbeCallback) pad_block, NULL, NULL); + (GstPadProbeCallback) sink_pad_block, NULL, NULL); webrtc->priv->pending_sink_transceivers = g_list_append (webrtc->priv->pending_sink_transceivers, gst_object_ref (pad)); @@ -4766,11 +5847,44 @@ gst_webrtc_bin_release_pad (GstElement * element, GstPad * pad) GstWebRTCBin *webrtc = GST_WEBRTC_BIN (element); GstWebRTCBinPad *webrtc_pad = GST_WEBRTC_BIN_PAD (pad); + GST_DEBUG_OBJECT (webrtc, "Releasing %" GST_PTR_FORMAT, webrtc_pad); + + /* remove the transceiver from the pad so that subsequent code doesn't use + * a possibly dead transceiver */ + PC_LOCK (webrtc); if (webrtc_pad->trans) gst_object_unref (webrtc_pad->trans); webrtc_pad->trans = NULL; + PC_UNLOCK (webrtc); _remove_pad (webrtc, webrtc_pad); + + PC_LOCK (webrtc); + _update_need_negotiation (webrtc); + PC_UNLOCK (webrtc); +} + +static void +_update_rtpstorage_latency (GstWebRTCBin * webrtc) +{ + guint i; + guint64 latency_ns; + + /* Add an extra 50 ms for safety */ + latency_ns = webrtc->priv->jb_latency + RTPSTORAGE_EXTRA_TIME; + latency_ns *= GST_MSECOND; + + for (i = 0; i < webrtc->priv->transports->len; i++) { + TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i); + GObject *storage = NULL; + + g_signal_emit_by_name (webrtc->rtpbin, "get-storage", stream->session_id, + &storage); + + g_object_set (storage, "size-time", latency_ns, NULL); + + g_object_unref (storage); + } } static void @@ -4781,8 +5895,12 @@ gst_webrtc_bin_set_property (GObject * object, guint prop_id, switch (prop_id) { case PROP_STUN_SERVER: + gst_webrtc_ice_set_stun_server (webrtc->priv->ice, + g_value_get_string (value)); + break; case PROP_TURN_SERVER: - g_object_set_property (G_OBJECT (webrtc->priv->ice), pspec->name, value); + gst_webrtc_ice_set_turn_server (webrtc->priv->ice, + g_value_get_string (value)); break; case PROP_BUNDLE_POLICY: if (g_value_get_enum (value) == GST_WEBRTC_BUNDLE_POLICY_BALANCED) { @@ -4793,9 +5911,14 @@ gst_webrtc_bin_set_property (GObject * object, guint prop_id, break; case PROP_ICE_TRANSPORT_POLICY: webrtc->ice_transport_policy = g_value_get_enum (value); - g_object_set (webrtc->priv->ice, "force-relay", + gst_webrtc_ice_set_force_relay (webrtc->priv->ice, webrtc->ice_transport_policy == - GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE, NULL); + GST_WEBRTC_ICE_TRANSPORT_POLICY_RELAY ? TRUE : FALSE); + break; + case PROP_LATENCY: + g_object_set_property (G_OBJECT (webrtc->rtpbin), "latency", value); + webrtc->priv->jb_latency = g_value_get_uint (value); + _update_rtpstorage_latency (webrtc); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -4852,8 +5975,12 @@ gst_webrtc_bin_get_property (GObject * object, guint prop_id, g_value_set_boxed (value, webrtc->pending_remote_description); break; case PROP_STUN_SERVER: + g_value_take_string (value, + gst_webrtc_ice_get_stun_server (webrtc->priv->ice)); + break; case PROP_TURN_SERVER: - g_object_get_property (G_OBJECT (webrtc->priv->ice), pspec->name, value); + g_value_take_string (value, + gst_webrtc_ice_get_turn_server (webrtc->priv->ice)); break; case PROP_BUNDLE_POLICY: g_value_set_enum (value, webrtc->bundle_policy); @@ -4861,6 +5988,12 @@ gst_webrtc_bin_get_property (GObject * object, guint prop_id, case PROP_ICE_TRANSPORT_POLICY: g_value_set_enum (value, webrtc->ice_transport_policy); break; + case PROP_ICE_AGENT: + g_value_set_object (value, webrtc->priv->ice); + break; + case PROP_LATENCY: + g_value_set_uint (value, webrtc->priv->jb_latency); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -4868,6 +6001,21 @@ gst_webrtc_bin_get_property (GObject * object, guint prop_id, PC_UNLOCK (webrtc); } +static void +gst_webrtc_bin_constructed (GObject * object) +{ + GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object); + gchar *name; + + name = g_strdup_printf ("%s:ice", GST_OBJECT_NAME (webrtc)); + webrtc->priv->ice = gst_webrtc_ice_new (name); + + gst_webrtc_ice_set_on_ice_candidate (webrtc->priv->ice, + (GstWebRTCIceOnCandidateFunc) _on_local_ice_candidate_cb, webrtc, NULL); + + g_free (name); +} + static void _free_pending_pad (GstPad * pad) { @@ -4898,24 +6046,28 @@ gst_webrtc_bin_finalize (GObject * object) GstWebRTCBin *webrtc = GST_WEBRTC_BIN (object); if (webrtc->priv->transports) - g_array_free (webrtc->priv->transports, TRUE); + g_ptr_array_free (webrtc->priv->transports, TRUE); webrtc->priv->transports = NULL; if (webrtc->priv->transceivers) - g_array_free (webrtc->priv->transceivers, TRUE); + g_ptr_array_free (webrtc->priv->transceivers, TRUE); webrtc->priv->transceivers = NULL; if (webrtc->priv->data_channels) - g_array_free (webrtc->priv->data_channels, TRUE); + g_ptr_array_free (webrtc->priv->data_channels, TRUE); webrtc->priv->data_channels = NULL; if (webrtc->priv->pending_data_channels) - g_array_free (webrtc->priv->pending_data_channels, TRUE); + g_ptr_array_free (webrtc->priv->pending_data_channels, TRUE); webrtc->priv->pending_data_channels = NULL; - if (webrtc->priv->pending_ice_candidates) - g_array_free (webrtc->priv->pending_ice_candidates, TRUE); - webrtc->priv->pending_ice_candidates = NULL; + if (webrtc->priv->pending_remote_ice_candidates) + g_array_free (webrtc->priv->pending_remote_ice_candidates, TRUE); + webrtc->priv->pending_remote_ice_candidates = NULL; + + if (webrtc->priv->pending_local_ice_candidates) + g_array_free (webrtc->priv->pending_local_ice_candidates, TRUE); + webrtc->priv->pending_local_ice_candidates = NULL; if (webrtc->priv->session_mid_map) g_array_free (webrtc->priv->session_mid_map, TRUE); @@ -4945,10 +6097,18 @@ gst_webrtc_bin_finalize (GObject * object) gst_webrtc_session_description_free (webrtc->pending_remote_description); webrtc->pending_remote_description = NULL; + if (webrtc->priv->last_generated_answer) + gst_webrtc_session_description_free (webrtc->priv->last_generated_answer); + webrtc->priv->last_generated_answer = NULL; + if (webrtc->priv->last_generated_offer) + gst_webrtc_session_description_free (webrtc->priv->last_generated_offer); + webrtc->priv->last_generated_offer = NULL; + if (webrtc->priv->stats) gst_structure_free (webrtc->priv->stats); webrtc->priv->stats = NULL; + g_mutex_clear (ICE_GET_LOCK (webrtc)); g_mutex_clear (PC_GET_LOCK (webrtc)); g_cond_clear (PC_GET_COND (webrtc)); @@ -4973,6 +6133,7 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) "Filter/Network/WebRTC", "A bin for webrtc connections", "Matthew Waters "); + gobject_class->constructed = gst_webrtc_bin_constructed; gobject_class->get_property = gst_webrtc_bin_get_property; gobject_class->set_property = gst_webrtc_bin_set_property; gobject_class->dispose = gst_webrtc_bin_dispose; @@ -4981,16 +6142,58 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) g_object_class_install_property (gobject_class, PROP_LOCAL_DESCRIPTION, g_param_spec_boxed ("local-description", "Local Description", - "The local SDP description to use for this connection", + "The local SDP description in use for this connection. " + "Favours a pending description over the current description", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_CURRENT_LOCAL_DESCRIPTION, + g_param_spec_boxed ("current-local-description", + "Current Local Description", + "The local description that was successfully negotiated the last time " + "the connection transitioned into the stable state", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_PENDING_LOCAL_DESCRIPTION, + g_param_spec_boxed ("pending-local-description", + "Pending Local Description", + "The local description that is in the process of being negotiated plus " + "any local candidates that have been generated by the ICE Agent since the " + "offer or answer was created", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_REMOTE_DESCRIPTION, g_param_spec_boxed ("remote-description", "Remote Description", - "The remote SDP description to use for this connection", + "The remote SDP description to use for this connection. " + "Favours a pending description over the current description", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_CURRENT_REMOTE_DESCRIPTION, + g_param_spec_boxed ("current-remote-description", + "Current Remote Description", + "The last remote description that was successfully negotiated the last " + "time the connection transitioned into the stable state plus any remote " + "candidates that have been supplied via addIceCandidate() since the offer " + "or answer was created", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_PENDING_REMOTE_DESCRIPTION, + g_param_spec_boxed ("pending-remote-description", + "Pending Remote Description", + "The remote description that is in the process of being negotiated, " + "complete with any remote candidates that have been supplied via " + "addIceCandidate() since the offer or answer was created", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_STUN_SERVER, @@ -5054,73 +6257,89 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) GST_WEBRTC_ICE_TRANSPORT_POLICY_ALL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_ICE_AGENT, + g_param_spec_object ("ice-agent", "WebRTC ICE agent", + "The WebRTC ICE agent", + GST_TYPE_WEBRTC_ICE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + /** + * GstWebRTCBin:latency: + * + * Default duration to buffer in the jitterbuffers (in ms) + * + * Since: 1.18 + */ + + g_object_class_install_property (gobject_class, + PROP_LATENCY, + g_param_spec_uint ("latency", "Latency", + "Default duration to buffer in the jitterbuffers (in ms)", + 0, G_MAXUINT, 200, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * GstWebRTCBin::create-offer: - * @object: the #GstWebRtcBin - * @options: create-offer options + * @object: the #webrtcbin + * @options: (nullable): create-offer options * @promise: a #GstPromise which will contain the offer */ gst_webrtc_bin_signals[CREATE_OFFER_SIGNAL] = g_signal_new_class_handler ("create-offer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, - GST_TYPE_PROMISE); + G_CALLBACK (gst_webrtc_bin_create_offer), NULL, NULL, NULL, + G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE); /** * GstWebRTCBin::create-answer: - * @object: the #GstWebRtcBin - * @options: create-answer options + * @object: the #webrtcbin + * @options: (nullable): create-answer options * @promise: a #GstPromise which will contain the answer */ gst_webrtc_bin_signals[CREATE_ANSWER_SIGNAL] = g_signal_new_class_handler ("create-answer", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, - GST_TYPE_PROMISE); + G_CALLBACK (gst_webrtc_bin_create_answer), NULL, NULL, NULL, + G_TYPE_NONE, 2, GST_TYPE_STRUCTURE, GST_TYPE_PROMISE); /** * GstWebRTCBin::set-local-description: - * @object: the #GstWebRtcBin + * @object: the #GstWebRTCBin * @desc: a #GstWebRTCSessionDescription description * @promise: (nullable): a #GstPromise to be notified when it's set */ gst_webrtc_bin_signals[SET_LOCAL_DESCRIPTION_SIGNAL] = g_signal_new_class_handler ("set-local-description", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 2, - GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE); + G_CALLBACK (gst_webrtc_bin_set_local_description), NULL, NULL, NULL, + G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE); /** * GstWebRTCBin::set-remote-description: - * @object: the #GstWebRtcBin + * @object: the #GstWebRTCBin * @desc: a #GstWebRTCSessionDescription description * @promise: (nullable): a #GstPromise to be notified when it's set */ gst_webrtc_bin_signals[SET_REMOTE_DESCRIPTION_SIGNAL] = g_signal_new_class_handler ("set-remote-description", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 2, - GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE); + G_CALLBACK (gst_webrtc_bin_set_remote_description), NULL, NULL, NULL, + G_TYPE_NONE, 2, GST_TYPE_WEBRTC_SESSION_DESCRIPTION, GST_TYPE_PROMISE); /** * GstWebRTCBin::add-ice-candidate: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin * @mline_index: the index of the media description in the SDP * @ice-candidate: an ice candidate */ gst_webrtc_bin_signals[ADD_ICE_CANDIDATE_SIGNAL] = g_signal_new_class_handler ("add-ice-candidate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); + G_CALLBACK (gst_webrtc_bin_add_ice_candidate), NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); /** * GstWebRTCBin::get-stats: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin * @pad: (nullable): A #GstPad to get the stats for, or %NULL for all * @promise: a #GstPromise for the result * @@ -5131,7 +6350,7 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) * and is constantly changing these statistics may be changed to fit with * the latest spec. * - * Each field key is a unique identifer for each RTCStats + * Each field key is a unique identifier for each RTCStats * (https://www.w3.org/TR/webrtc/#rtcstats-dictionary) value (another * GstStructure) in the RTCStatsReport * (https://www.w3.org/TR/webrtc/#rtcstatsreport-object). Each supported @@ -5191,53 +6410,51 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) gst_webrtc_bin_signals[GET_STATS_SIGNAL] = g_signal_new_class_handler ("get-stats", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_PAD, - GST_TYPE_PROMISE); + G_CALLBACK (gst_webrtc_bin_get_stats), NULL, NULL, NULL, + G_TYPE_NONE, 2, GST_TYPE_PAD, GST_TYPE_PROMISE); /** * GstWebRTCBin::on-negotiation-needed: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin */ gst_webrtc_bin_signals[ON_NEGOTIATION_NEEDED_SIGNAL] = g_signal_new ("on-negotiation-needed", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 0); + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * GstWebRTCBin::on-ice-candidate: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin * @mline_index: the index of the media description in the SDP * @candidate: the ICE candidate */ gst_webrtc_bin_signals[ON_ICE_CANDIDATE_SIGNAL] = g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); /** * GstWebRTCBin::on-new-transceiver: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin * @candidate: the new #GstWebRTCRTPTransceiver */ gst_webrtc_bin_signals[ON_NEW_TRANSCEIVER_SIGNAL] = g_signal_new ("on-new-transceiver", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_WEBRTC_RTP_TRANSCEIVER); /** * GstWebRTCBin::on-data-channel: - * @object: the #GstWebRtcBin - * @candidate: the new #GstWebRTCDataChannel + * @object: the #GstWebRTCBin + * @candidate: the new `GstWebRTCDataChannel` */ gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL] = g_signal_new ("on-data-channel", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_WEBRTC_DATA_CHANNEL); /** * GstWebRTCBin::add-transceiver: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin * @direction: the direction of the new transceiver * @caps: (allow none): the codec preferences for this transceiver * @@ -5247,39 +6464,38 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) g_signal_new_class_handler ("add-transceiver", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_CALLBACK (gst_webrtc_bin_add_transceiver), NULL, NULL, - g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2, + NULL, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 2, GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, GST_TYPE_CAPS); /** * GstWebRTCBin::get-transceivers: - * @object: the #GstWebRtcBin + * @object: the #webrtcbin * * Returns: a #GArray of #GstWebRTCRTPTransceivers */ gst_webrtc_bin_signals[GET_TRANSCEIVERS_SIGNAL] = g_signal_new_class_handler ("get-transceivers", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_ARRAY, 0); + G_CALLBACK (gst_webrtc_bin_get_transceivers), NULL, NULL, NULL, + G_TYPE_ARRAY, 0); /** * GstWebRTCBin::get-transceiver: - * @object: the #GstWebRtcBin + * @object: the #GstWebRTCBin * @idx: The index of the transceiver * - * Returns: the #GstWebRTCRTPTransceiver, or %NULL + * Returns: (transfer full): the #GstWebRTCRTPTransceiver, or %NULL * Since: 1.16 */ gst_webrtc_bin_signals[GET_TRANSCEIVER_SIGNAL] = g_signal_new_class_handler ("get-transceiver", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, - g_cclosure_marshal_generic, GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, - G_TYPE_INT); + G_CALLBACK (gst_webrtc_bin_get_transceiver), NULL, NULL, NULL, + GST_TYPE_WEBRTC_RTP_TRANSCEIVER, 1, G_TYPE_INT); /** * GstWebRTCBin::add-turn-server: - * @object: the #GstWebRtcBin + * @object: the #GstWebRTCBin * @uri: The uri of the server of the form turn(s)://username:password@host:port * * Add a turn server to obtain ICE candidates from @@ -5287,12 +6503,12 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) gst_webrtc_bin_signals[ADD_TURN_SERVER_SIGNAL] = g_signal_new_class_handler ("add-turn-server", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); + G_CALLBACK (gst_webrtc_bin_add_turn_server), NULL, NULL, NULL, + G_TYPE_BOOLEAN, 1, G_TYPE_STRING); /* * GstWebRTCBin::create-data-channel: - * @object: the #GstWebRtcBin + * @object: the #GstWebRTCBin * @label: the label for the data channel * @options: a #GstStructure of options for creating the data channel * @@ -5300,11 +6516,11 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) * members outlined https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit and * and reproduced below * - * ordered G_TYPE_BOOLEAN Whether the channal will send data with guarenteed ordering + * ordered G_TYPE_BOOLEAN Whether the channal will send data with guaranteed ordering * max-packet-lifetime G_TYPE_INT The time in milliseconds to attempt transmitting unacknowledged data. -1 for unset * max-retransmits G_TYPE_INT The number of times data will be attempted to be transmitted without acknowledgement before dropping * protocol G_TYPE_STRING The subprotocol used by this channel - * negotiated G_TYPE_BOOLEAN Whether the created data channel should not perform in-band chnanel announcment. If %TRUE, then application must negotiate the channel itself and create the corresponding channel on the peer with the same id. + * negotiated G_TYPE_BOOLEAN Whether the created data channel should not perform in-band chnanel announcement. If %TRUE, then application must negotiate the channel itself and create the corresponding channel on the peer with the same id. * id G_TYPE_INT Override the default identifier selection of this channel * priority GST_TYPE_WEBRTC_PRIORITY_TYPE The priority to use for this channel * @@ -5314,24 +6530,28 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) g_signal_new_class_handler ("create-data-channel", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_CALLBACK (gst_webrtc_bin_create_data_channel), NULL, NULL, - g_cclosure_marshal_generic, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, - G_TYPE_STRING, GST_TYPE_STRUCTURE); + NULL, GST_TYPE_WEBRTC_DATA_CHANNEL, 2, G_TYPE_STRING, GST_TYPE_STRUCTURE); + +#ifndef TIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK + gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_BIN_PAD, 0); + gst_type_mark_as_plugin_api (GST_TYPE_WEBRTC_ICE, 0); +#endif } static void -_deref_unparent_and_unref (GObject ** object) +_unparent_and_unref (GObject * object) { - GstObject *obj = GST_OBJECT (*object); + GstObject *obj = GST_OBJECT (object); GST_OBJECT_PARENT (obj) = NULL; - gst_object_unref (*object); + gst_object_unref (obj); } static void -_transport_free (GObject ** object) +_transport_free (GObject * object) { - TransportStream *stream = (TransportStream *) * object; + TransportStream *stream = (TransportStream *) object; GstWebRTCBin *webrtc; webrtc = GST_WEBRTC_BIN (GST_OBJECT_PARENT (stream)); @@ -5346,7 +6566,7 @@ _transport_free (GObject ** object) g_signal_handlers_disconnect_by_data (stream->rtcp_transport, webrtc); } - gst_object_unref (*object); + gst_object_unref (object); } static void @@ -5356,39 +6576,37 @@ gst_webrtc_bin_init (GstWebRTCBin * webrtc) g_mutex_init (PC_GET_LOCK (webrtc)); g_cond_init (PC_GET_COND (webrtc)); + g_mutex_init (ICE_GET_LOCK (webrtc)); + webrtc->rtpbin = _create_rtpbin (webrtc); gst_bin_add (GST_BIN (webrtc), webrtc->rtpbin); - webrtc->priv->transceivers = g_array_new (FALSE, TRUE, sizeof (gpointer)); - g_array_set_clear_func (webrtc->priv->transceivers, - (GDestroyNotify) _deref_unparent_and_unref); - - webrtc->priv->transports = g_array_new (FALSE, TRUE, sizeof (gpointer)); - g_array_set_clear_func (webrtc->priv->transports, - (GDestroyNotify) _transport_free); + webrtc->priv->transceivers = + g_ptr_array_new_with_free_func ((GDestroyNotify) _unparent_and_unref); + webrtc->priv->transports = + g_ptr_array_new_with_free_func ((GDestroyNotify) _transport_free); - webrtc->priv->data_channels = g_array_new (FALSE, TRUE, sizeof (gpointer)); - g_array_set_clear_func (webrtc->priv->data_channels, - (GDestroyNotify) _deref_and_unref); + webrtc->priv->data_channels = + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); webrtc->priv->pending_data_channels = - g_array_new (FALSE, TRUE, sizeof (gpointer)); - g_array_set_clear_func (webrtc->priv->pending_data_channels, - (GDestroyNotify) _deref_and_unref); + g_ptr_array_new_with_free_func ((GDestroyNotify) gst_object_unref); webrtc->priv->session_mid_map = g_array_new (FALSE, TRUE, sizeof (SessionMidItem)); g_array_set_clear_func (webrtc->priv->session_mid_map, (GDestroyNotify) clear_session_mid_item); - webrtc->priv->ice = gst_webrtc_ice_new (); - g_signal_connect (webrtc->priv->ice, "on-ice-candidate", - G_CALLBACK (_on_ice_candidate), webrtc); webrtc->priv->ice_stream_map = g_array_new (FALSE, TRUE, sizeof (IceStreamItem)); - webrtc->priv->pending_ice_candidates = - g_array_new (FALSE, TRUE, sizeof (IceCandidateItem *)); - g_array_set_clear_func (webrtc->priv->pending_ice_candidates, + webrtc->priv->pending_remote_ice_candidates = + g_array_new (FALSE, TRUE, sizeof (IceCandidateItem)); + g_array_set_clear_func (webrtc->priv->pending_remote_ice_candidates, + (GDestroyNotify) _clear_ice_candidate_item); + + webrtc->priv->pending_local_ice_candidates = + g_array_new (FALSE, TRUE, sizeof (IceCandidateItem)); + g_array_set_clear_func (webrtc->priv->pending_local_ice_candidates, (GDestroyNotify) _clear_ice_candidate_item); /* we start off closed until we move to READY */ diff --git a/ext/webrtc/gstwebrtcbin.h b/ext/webrtc/gstwebrtcbin.h index 84158e887..e4b462f2e 100644 --- a/ext/webrtc/gstwebrtcbin.h +++ b/ext/webrtc/gstwebrtcbin.h @@ -96,20 +96,24 @@ struct _GstWebRTCBinPrivate guint max_sink_pad_serial; gboolean bundle; - GArray *transceivers; + GPtrArray *transceivers; GArray *session_mid_map; - GArray *transports; - GArray *data_channels; + GPtrArray *transports; + GPtrArray *data_channels; /* list of data channels we've received a sctp stream for but no data * channel protocol for */ - GArray *pending_data_channels; + GPtrArray *pending_data_channels; + + guint jb_latency; GstWebRTCSCTPTransport *sctp_transport; TransportStream *data_channel_transport; GstWebRTCICE *ice; GArray *ice_stream_map; - GArray *pending_ice_candidates; + GMutex ice_lock; + GArray *pending_remote_ice_candidates; + GArray *pending_local_ice_candidates; /* peerconnection variables */ gboolean is_closed; @@ -131,6 +135,10 @@ struct _GstWebRTCBinPrivate /* count of the number of media streams we've offered for uniqueness */ /* FIXME: overflow? */ guint media_counter; + /* the number of times create_offer has been called for the version field */ + guint offer_count; + GstWebRTCSessionDescription *last_generated_offer; + GstWebRTCSessionDescription *last_generated_answer; GstStructure *stats; }; @@ -143,13 +151,14 @@ typedef struct GstWebRTCBinFunc op; gpointer data; GDestroyNotify notify; -// GstPromise *promise; /* FIXME */ + GstPromise *promise; } GstWebRTCBinTask; -void gst_webrtc_bin_enqueue_task (GstWebRTCBin * pc, +gboolean gst_webrtc_bin_enqueue_task (GstWebRTCBin * pc, GstWebRTCBinFunc func, gpointer data, - GDestroyNotify notify); + GDestroyNotify notify, + GstPromise *promise); G_END_DECLS diff --git a/ext/webrtc/gstwebrtcice.c b/ext/webrtc/gstwebrtcice.c index 8887b3358..7dd716ff7 100644 --- a/ext/webrtc/gstwebrtcice.c +++ b/ext/webrtc/gstwebrtcice.c @@ -46,20 +46,16 @@ gst_webrtc_ice_error_quark (void) enum { SIGNAL_0, - ON_ICE_CANDIDATE_SIGNAL, - ON_ICE_GATHERING_STATE_CHANGE_SIGNAL, + ADD_LOCAL_IP_ADDRESS_SIGNAL, LAST_SIGNAL, }; enum { PROP_0, - PROP_ICE_GATHERING_STATE, - PROP_STUN_SERVER, - PROP_TURN_SERVER, - PROP_CONTROLLER, PROP_AGENT, - PROP_FORCE_RELAY, + PROP_ICE_TCP, + PROP_ICE_UDP, }; static guint gst_webrtc_ice_signals[LAST_SIGNAL] = { 0 }; @@ -75,6 +71,10 @@ struct _GstWebRTCICEPrivate GMainLoop *loop; GMutex lock; GCond cond; + + GstWebRTCIceOnCandidateFunc on_candidate; + gpointer on_candidate_data; + GDestroyNotify on_candidate_notify; }; #define gst_webrtc_ice_parent_class parent_class @@ -118,7 +118,7 @@ static void _start_thread (GstWebRTCICE * ice) { g_mutex_lock (&ice->priv->lock); - ice->priv->thread = g_thread_new ("gst-nice-ops", + ice->priv->thread = g_thread_new (GST_OBJECT_NAME (ice), (GThreadFunc) _gst_nice_thread, ice); while (!ice->priv->loop) @@ -286,13 +286,19 @@ _parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass) colon = g_strstr_len (userinfo, -1, ":"); if (!colon) { - *user = g_strdup (userinfo); + *user = g_uri_unescape_string (userinfo, NULL); *pass = NULL; return; } - *user = g_strndup (userinfo, colon - userinfo); - *pass = g_strdup (&colon[1]); + /* Check that the first occurence is also the last occurence */ + if (colon != g_strrstr (userinfo, ":")) + GST_WARNING ("userinfo %s contains more than one ':', will assume that the " + "first ':' delineates user:pass. You should escape the user and pass " + "before adding to the URI.", userinfo); + + *user = g_uri_unescape_segment (userinfo, colon, NULL); + *pass = g_uri_unescape_string (&colon[1], NULL); } static gchar * @@ -366,8 +372,8 @@ _add_turn_server (GstWebRTCICE * ice, struct NiceStreamItem *item, for (i = 0; i < relay_n; i++) { ret = nice_agent_set_relay_info (ice->priv->nice_agent, item->nice_stream_id, NICE_COMPONENT_TYPE_RTP, - gst_uri_get_host (turn_server), gst_uri_get_port (turn_server), user, - pass, relays[i]); + gst_uri_get_host (turn_server), gst_uri_get_port (turn_server), + user, pass, relays[i]); if (!ret) { gchar *uri = gst_uri_to_string (turn_server); GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri); @@ -376,8 +382,8 @@ _add_turn_server (GstWebRTCICE * ice, struct NiceStreamItem *item, } ret = nice_agent_set_relay_info (ice->priv->nice_agent, item->nice_stream_id, NICE_COMPONENT_TYPE_RTCP, - gst_uri_get_host (turn_server), gst_uri_get_port (turn_server), user, - pass, relays[i]); + gst_uri_get_host (turn_server), gst_uri_get_port (turn_server), + user, pass, relays[i]); if (!ret) { gchar *uri = gst_uri_to_string (turn_server); GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri); @@ -414,9 +420,8 @@ _add_stun_server (GstWebRTCICE * ice, GstUri * stun_server) gchar *ip = NULL; guint port; - GST_DEBUG_OBJECT (ice, "adding stun server, %s", s); - s = gst_uri_to_string (stun_server); + GST_DEBUG_OBJECT (ice, "adding stun server, %s", s); host = gst_uri_get_host (stun_server); if (!host) { @@ -513,8 +518,11 @@ _on_new_candidate (NiceAgent * agent, NiceCandidate * candidate, } attr = nice_agent_generate_local_candidate_sdp (agent, candidate); - g_signal_emit (ice, gst_webrtc_ice_signals[ON_ICE_CANDIDATE_SIGNAL], - 0, item->session_id, attr); + + if (ice->priv->on_candidate) + ice->priv->on_candidate (ice, item->session_id, attr, + ice->priv->on_candidate_data); + g_free (attr); } @@ -603,6 +611,47 @@ _candidate_type_to_string (NiceCandidateType type) } #endif +/* parse the address for possible resolution */ +static gboolean +get_candidate_address (const gchar * candidate, gchar ** prefix, + gchar ** address, gchar ** postfix) +{ + char **tokens = NULL; + + if (!g_str_has_prefix (candidate, "a=candidate:")) { + GST_ERROR ("candidate \"%s\" does not start with \"a=candidate:\"", + candidate); + goto failure; + } + + if (!(tokens = g_strsplit (candidate, " ", 6))) { + GST_ERROR ("candidate \"%s\" could not be tokenized", candidate); + goto failure; + } + + if (g_strv_length (tokens) < 6) { + GST_ERROR ("candidate \"%s\" tokenization resulted in not enough tokens", + candidate); + goto failure; + } + + if (address) + *address = g_strdup (tokens[4]); + tokens[4] = NULL; + if (prefix) + *prefix = g_strjoinv (" ", tokens); + if (postfix) + *postfix = g_strdup (tokens[5]); + + g_strfreev (tokens); + return TRUE; + +failure: + if (tokens) + g_strfreev (tokens); + return FALSE; +} + /* must start with "a=candidate:" */ void gst_webrtc_ice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream, @@ -619,8 +668,58 @@ gst_webrtc_ice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream, nice_agent_parse_remote_candidate_sdp (ice->priv->nice_agent, item->nice_stream_id, candidate); if (!cand) { - GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'", candidate); - return; + /* might be a .local candidate */ + char *prefix = NULL, *address = NULL, *postfix = NULL; + char *new_addr, *new_candidate; + char *new_candv[4] = { NULL, }; + + if (!get_candidate_address (candidate, &prefix, &address, &postfix)) { + GST_WARNING_OBJECT (ice, "Failed to retrieve address from candidate %s", + candidate); + goto fail; + } + + if (!g_str_has_suffix (address, ".local")) { + GST_WARNING_OBJECT (ice, "candidate address \'%s\' does not end " + "with \'.local\'", address); + goto fail; + } + + /* FIXME: async */ + if (!(new_addr = _resolve_host (ice, address))) { + GST_WARNING_OBJECT (ice, "Failed to resolve %s", address); + goto fail; + } + + new_candv[0] = prefix; + new_candv[1] = new_addr; + new_candv[2] = postfix; + new_candv[3] = NULL; + new_candidate = g_strjoinv (" ", new_candv); + + GST_DEBUG_OBJECT (ice, "resolved to candidate %s", new_candidate); + + cand = + nice_agent_parse_remote_candidate_sdp (ice->priv->nice_agent, + item->nice_stream_id, new_candidate); + g_free (new_candidate); + if (!cand) { + GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'", + new_candidate); + goto fail; + } + + g_free (prefix); + g_free (new_addr); + g_free (postfix); + + if (0) { + fail: + g_free (prefix); + g_free (address); + g_free (postfix); + return; + } } candidates = g_slist_append (candidates, cand); @@ -669,6 +768,28 @@ done: return ret; } +static gboolean +gst_webrtc_ice_add_local_ip_address (GstWebRTCICE * ice, const gchar * address) +{ + gboolean ret = FALSE; + NiceAddress nice_addr; + + nice_address_init (&nice_addr); + + ret = nice_address_set_from_string (&nice_addr, address); + + if (ret) { + ret = nice_agent_add_local_address (ice->priv->nice_agent, &nice_addr); + if (!ret) { + GST_ERROR_OBJECT (ice, "Failed to add local address to NiceAgent"); + } + } else { + GST_ERROR_OBJECT (ice, "Failed to initialize NiceAddress [%s]", address); + } + + return ret; +} + gboolean gst_webrtc_ice_set_local_credentials (GstWebRTCICE * ice, GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd) @@ -704,6 +825,42 @@ gst_webrtc_ice_gather_candidates (GstWebRTCICE * ice, return gst_webrtc_ice_stream_gather_candidates (stream); } +void +gst_webrtc_ice_set_is_controller (GstWebRTCICE * ice, gboolean controller) +{ + g_object_set (G_OBJECT (ice->priv->nice_agent), "controlling-mode", + controller, NULL); +} + +gboolean +gst_webrtc_ice_get_is_controller (GstWebRTCICE * ice) +{ + gboolean ret; + g_object_get (G_OBJECT (ice->priv->nice_agent), "controlling-mode", + &ret, NULL); + return ret; +} + +void +gst_webrtc_ice_set_force_relay (GstWebRTCICE * ice, gboolean force_relay) +{ + g_object_set (G_OBJECT (ice->priv->nice_agent), "force-relay", force_relay, + NULL); +} + +void +gst_webrtc_ice_set_on_ice_candidate (GstWebRTCICE * ice, + GstWebRTCIceOnCandidateFunc func, gpointer user_data, GDestroyNotify notify) +{ + if (ice->priv->on_candidate_notify) + ice->priv->on_candidate_notify (ice->priv->on_candidate_data); + ice->priv->on_candidate = NULL; + + ice->priv->on_candidate = func; + ice->priv->on_candidate_data = user_data; + ice->priv->on_candidate_notify = notify; +} + static void _clear_ice_stream (struct NiceStreamItem *item) { @@ -720,7 +877,7 @@ _clear_ice_stream (struct NiceStreamItem *item) static GstUri * _validate_turn_server (GstWebRTCICE * ice, const gchar * s) { - GstUri *uri = gst_uri_from_string (s); + GstUri *uri = gst_uri_from_string_escaped (s); const gchar *userinfo, *scheme; GList *keys = NULL, *l; gchar *user = NULL, *pass = NULL; @@ -792,6 +949,54 @@ out: return uri; } +void +gst_webrtc_ice_set_stun_server (GstWebRTCICE * ice, const gchar * uri_s) +{ + GstUri *uri = gst_uri_from_string_escaped (uri_s); + const gchar *msg = "must be of the form stun://:"; + + GST_DEBUG_OBJECT (ice, "setting stun server, %s", uri_s); + + if (!uri) { + GST_ERROR_OBJECT (ice, "Couldn't parse stun server '%s', %s", uri_s, msg); + return; + } + + if (ice->stun_server) + gst_uri_unref (ice->stun_server); + ice->stun_server = uri; +} + +gchar * +gst_webrtc_ice_get_stun_server (GstWebRTCICE * ice) +{ + if (ice->stun_server) + return gst_uri_to_string (ice->stun_server); + else + return NULL; +} + +void +gst_webrtc_ice_set_turn_server (GstWebRTCICE * ice, const gchar * uri_s) +{ + GstUri *uri = _validate_turn_server (ice, uri_s); + + if (uri) { + if (ice->turn_server) + gst_uri_unref (ice->turn_server); + ice->turn_server = uri; + } +} + +gchar * +gst_webrtc_ice_get_turn_server (GstWebRTCICE * ice) +{ + if (ice->turn_server) + return gst_uri_to_string (ice->turn_server); + else + return NULL; +} + static void gst_webrtc_ice_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -799,40 +1004,13 @@ gst_webrtc_ice_set_property (GObject * object, guint prop_id, GstWebRTCICE *ice = GST_WEBRTC_ICE (object); switch (prop_id) { - case PROP_STUN_SERVER:{ - const gchar *s = g_value_get_string (value); - GstUri *uri = gst_uri_from_string (s); - const gchar *msg = "must be of the form stun://:"; - - GST_DEBUG_OBJECT (ice, "setting stun server, %s", s); - - if (!uri) { - GST_ERROR_OBJECT (ice, "Couldn't parse stun server '%s', %s", s, msg); - return; - } - - if (ice->stun_server) - gst_uri_unref (ice->stun_server); - ice->stun_server = uri; - break; - } - case PROP_TURN_SERVER:{ - GstUri *uri = _validate_turn_server (ice, g_value_get_string (value)); - - if (uri) { - if (ice->turn_server) - gst_uri_unref (ice->turn_server); - ice->turn_server = uri; - } - break; - } - case PROP_CONTROLLER: + case PROP_ICE_TCP: g_object_set_property (G_OBJECT (ice->priv->nice_agent), - "controlling-mode", value); + "ice-tcp", value); break; - case PROP_FORCE_RELAY: + case PROP_ICE_UDP: g_object_set_property (G_OBJECT (ice->priv->nice_agent), - "force-relay", value); + "ice-udp", value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -847,28 +1025,16 @@ gst_webrtc_ice_get_property (GObject * object, guint prop_id, GstWebRTCICE *ice = GST_WEBRTC_ICE (object); switch (prop_id) { - case PROP_STUN_SERVER: - if (ice->stun_server) - g_value_take_string (value, gst_uri_to_string (ice->stun_server)); - else - g_value_take_string (value, NULL); - break; - case PROP_TURN_SERVER: - if (ice->turn_server) - g_value_take_string (value, gst_uri_to_string (ice->turn_server)); - else - g_value_take_string (value, NULL); - break; - case PROP_CONTROLLER: - g_object_get_property (G_OBJECT (ice->priv->nice_agent), - "controlling-mode", value); - break; case PROP_AGENT: g_value_set_object (value, ice->priv->nice_agent); break; - case PROP_FORCE_RELAY: + case PROP_ICE_TCP: + g_object_get_property (G_OBJECT (ice->priv->nice_agent), + "ice-tcp", value); + break; + case PROP_ICE_UDP: g_object_get_property (G_OBJECT (ice->priv->nice_agent), - "force-relay", value); + "ice-udp", value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -885,6 +1051,11 @@ gst_webrtc_ice_finalize (GObject * object) _stop_thread (ice); + if (ice->priv->on_candidate_notify) + ice->priv->on_candidate_notify (ice->priv->on_candidate_data); + ice->priv->on_candidate = NULL; + ice->priv->on_candidate_notify = NULL; + if (ice->turn_server) gst_uri_unref (ice->turn_server); if (ice->stun_server) @@ -902,55 +1073,67 @@ gst_webrtc_ice_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +gst_webrtc_ice_constructed (GObject * object) +{ + GstWebRTCICE *ice = GST_WEBRTC_ICE (object); + + _start_thread (ice); + + ice->priv->nice_agent = nice_agent_new (ice->priv->main_context, + NICE_COMPATIBILITY_RFC5245); + g_signal_connect (ice->priv->nice_agent, "new-candidate-full", + G_CALLBACK (_on_new_candidate), ice); + + G_OBJECT_CLASS (parent_class)->constructed (object); +} + static void gst_webrtc_ice_class_init (GstWebRTCICEClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + gobject_class->constructed = gst_webrtc_ice_constructed; gobject_class->get_property = gst_webrtc_ice_get_property; gobject_class->set_property = gst_webrtc_ice_set_property; gobject_class->finalize = gst_webrtc_ice_finalize; - g_object_class_install_property (gobject_class, - PROP_STUN_SERVER, - g_param_spec_string ("stun-server", "STUN Server", - "The STUN server of the form stun://hostname:port", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_TURN_SERVER, - g_param_spec_string ("turn-server", "TURN Server", - "The TURN server of the form turn(s)://username:password@host:port", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_CONTROLLER, - g_param_spec_boolean ("controller", "ICE controller", - "Whether the ICE agent is the controller or controlled. " - "In WebRTC, the initial offerrer is the ICE controller.", FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_AGENT, g_param_spec_object ("agent", "ICE agent", - "ICE agent in use by this object", NICE_TYPE_AGENT, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + "ICE agent in use by this object. WARNING! Accessing this property " + "may have disastrous consequences for the operation of webrtcbin. " + "Other ICE implementations may not have the same interface.", + NICE_TYPE_AGENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, - PROP_FORCE_RELAY, - g_param_spec_boolean ("force-relay", "Force Relay", - "Force all traffic to go through a relay.", FALSE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + PROP_ICE_TCP, + g_param_spec_boolean ("ice-tcp", "ICE TCP", + "Whether the agent should use ICE-TCP when gathering candidates", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ICE_UDP, + g_param_spec_boolean ("ice-udp", "ICE UDP", + "Whether the agent should use ICE-UDP when gathering candidates", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /** - * GstWebRTCICE::on-ice-candidate: - * @object: the #GstWebRtcBin - * @candidate: the ICE candidate + * GstWebRTCICE::add-local-ip-address: + * @object: the #GstWebRTCICE + * @address: The local IP address + * + * Add a local IP address to use for ICE candidate gathering. If none + * are supplied, they will be discovered automatically. Calling this signal + * stops automatic ICE gathering. + * + * Returns: whether the address could be added. */ - gst_webrtc_ice_signals[ON_ICE_CANDIDATE_SIGNAL] = - g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); + gst_webrtc_ice_signals[ADD_LOCAL_IP_ADDRESS_SIGNAL] = + g_signal_new_class_handler ("add-local-ip-address", + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_webrtc_ice_add_local_ip_address), NULL, NULL, + g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); } static void @@ -965,13 +1148,6 @@ gst_webrtc_ice_init (GstWebRTCICE * ice) g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gst_uri_unref); - _start_thread (ice); - - ice->priv->nice_agent = nice_agent_new (ice->priv->main_context, - NICE_COMPATIBILITY_RFC5245); - g_signal_connect (ice->priv->nice_agent, "new-candidate-full", - G_CALLBACK (_on_new_candidate), ice); - ice->priv->nice_stream_map = g_array_new (FALSE, TRUE, sizeof (struct NiceStreamItem)); g_array_set_clear_func (ice->priv->nice_stream_map, @@ -979,7 +1155,7 @@ gst_webrtc_ice_init (GstWebRTCICE * ice) } GstWebRTCICE * -gst_webrtc_ice_new (void) +gst_webrtc_ice_new (const gchar * name) { - return g_object_new (GST_TYPE_WEBRTC_ICE, NULL); + return g_object_new (GST_TYPE_WEBRTC_ICE, "name", name, NULL); } diff --git a/ext/webrtc/gstwebrtcice.h b/ext/webrtc/gstwebrtcice.h index 4b0cfdce3..d7c096550 100644 --- a/ext/webrtc/gstwebrtcice.h +++ b/ext/webrtc/gstwebrtcice.h @@ -58,7 +58,7 @@ struct _GstWebRTCICEClass GstObjectClass parent_class; }; -GstWebRTCICE * gst_webrtc_ice_new (void); +GstWebRTCICE * gst_webrtc_ice_new (const gchar * name); GstWebRTCICEStream * gst_webrtc_ice_add_stream (GstWebRTCICE * ice, guint session_id); GstWebRTCICETransport * gst_webrtc_ice_find_transport (GstWebRTCICE * ice, @@ -82,6 +82,25 @@ gboolean gst_webrtc_ice_set_remote_credentials (GstWebRTCIC gboolean gst_webrtc_ice_add_turn_server (GstWebRTCICE * ice, const gchar * uri); +void gst_webrtc_ice_set_is_controller (GstWebRTCICE * ice, + gboolean controller); +gboolean gst_webrtc_ice_get_is_controller (GstWebRTCICE * ice); +void gst_webrtc_ice_set_force_relay (GstWebRTCICE * ice, + gboolean force_relay); +void gst_webrtc_ice_set_stun_server (GstWebRTCICE * ice, + const gchar * uri); +gchar * gst_webrtc_ice_get_stun_server (GstWebRTCICE * ice); +void gst_webrtc_ice_set_turn_server (GstWebRTCICE * ice, + const gchar * uri); +gchar * gst_webrtc_ice_get_turn_server (GstWebRTCICE * ice); + +typedef void (*GstWebRTCIceOnCandidateFunc) (GstWebRTCICE * ice, guint stream_id, gchar * candidate, gpointer user_data); + +void gst_webrtc_ice_set_on_ice_candidate (GstWebRTCICE * ice, + GstWebRTCIceOnCandidateFunc func, + gpointer user_data, + GDestroyNotify notify); + G_END_DECLS #endif /* __GST_WEBRTC_ICE_H__ */ diff --git a/ext/webrtc/gstwebrtcstats.c b/ext/webrtc/gstwebrtcstats.c index bf3830413..7ecf9b9aa 100644 --- a/ext/webrtc/gstwebrtcstats.c +++ b/ext/webrtc/gstwebrtcstats.c @@ -82,6 +82,8 @@ _get_peer_connection_stats (GstWebRTCBin * webrtc) } #define CLOCK_RATE_VALUE_TO_SECONDS(v,r) ((double) v / (double) clock_rate) +#define FIXED_16_16_TO_DOUBLE(v) ((double) ((v & 0xffff0000) >> 16) + ((v & 0xffff) / 65536.0)) +#define FIXED_32_32_TO_DOUBLE(v) ((double) ((v & G_GUINT64_CONSTANT (0xffffffff00000000)) >> 32) + ((v & G_GUINT64_CONSTANT (0xffffffff)) / 4294967296.0)) /* https://www.w3.org/TR/webrtc-stats/#inboundrtpstats-dict* https://www.w3.org/TR/webrtc-stats/#outboundrtpstats-dict* */ @@ -90,52 +92,43 @@ _get_stats_from_rtp_source_stats (GstWebRTCBin * webrtc, const GstStructure * source_stats, const gchar * codec_id, const gchar * transport_id, GstStructure * s) { - GstStructure *in, *out, *r_in, *r_out; - gchar *in_id, *out_id, *r_in_id, *r_out_id; guint ssrc, fir, pli, nack, jitter; int lost, clock_rate; guint64 packets, bytes; - gboolean have_rb = FALSE, sent_rb = FALSE; + gboolean internal; double ts; gst_structure_get_double (s, "timestamp", &ts); - gst_structure_get_uint (source_stats, "ssrc", &ssrc); - gst_structure_get (source_stats, "have-rb", G_TYPE_BOOLEAN, &have_rb, - "sent_rb", G_TYPE_BOOLEAN, &sent_rb, "clock-rate", G_TYPE_INT, - &clock_rate, NULL); - - in_id = g_strdup_printf ("rtp-inbound-stream-stats_%u", ssrc); - out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc); - r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc); - r_out_id = g_strdup_printf ("rtp-remote-outbound-stream-stats_%u", ssrc); - - in = gst_structure_new_empty (in_id); - _set_base_stats (in, GST_WEBRTC_STATS_INBOUND_RTP, ts, in_id); - - /* RTCStreamStats */ - gst_structure_set (in, "ssrc", G_TYPE_UINT, ssrc, NULL); - gst_structure_set (in, "codec-id", G_TYPE_STRING, codec_id, NULL); - gst_structure_set (in, "transport-id", G_TYPE_STRING, transport_id, NULL); - if (gst_structure_get_uint (source_stats, "recv-fir-count", &fir)) - gst_structure_set (in, "fir-count", G_TYPE_UINT, fir, NULL); - if (gst_structure_get_uint (source_stats, "recv-pli-count", &pli)) - gst_structure_set (in, "pli-count", G_TYPE_UINT, pli, NULL); - if (gst_structure_get_uint (source_stats, "recv-nack-count", &nack)) - gst_structure_set (in, "nack-count", G_TYPE_UINT, nack, NULL); - /* XXX: mediaType, trackId, sliCount, qpSum */ - - /* RTCReceivedRTPStreamStats */ - if (gst_structure_get_uint64 (source_stats, "packets-received", &packets)) - gst_structure_set (in, "packets-received", G_TYPE_UINT64, packets, NULL); - if (gst_structure_get_uint64 (source_stats, "octets-received", &bytes)) - gst_structure_set (in, "bytes-received", G_TYPE_UINT64, bytes, NULL); - if (gst_structure_get_int (source_stats, "packets-lost", &lost)) - gst_structure_set (in, "packets-lost", G_TYPE_INT, lost, NULL); - if (gst_structure_get_uint (source_stats, "jitter", &jitter)) - gst_structure_set (in, "jitter", G_TYPE_DOUBLE, - CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL); -/* - RTCReceivedRTPStreamStats + gst_structure_get (source_stats, "ssrc", G_TYPE_UINT, &ssrc, "clock-rate", + G_TYPE_INT, &clock_rate, "internal", G_TYPE_BOOLEAN, &internal, NULL); + + if (internal) { + GstStructure *r_in, *out; + gchar *out_id, *r_in_id; + + out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc); + r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc); + + r_in = gst_structure_new_empty (r_in_id); + _set_base_stats (r_in, GST_WEBRTC_STATS_REMOTE_INBOUND_RTP, ts, r_in_id); + + /* RTCStreamStats */ + gst_structure_set (r_in, "local-id", G_TYPE_STRING, out_id, NULL); + gst_structure_set (r_in, "ssrc", G_TYPE_UINT, ssrc, NULL); + gst_structure_set (r_in, "codec-id", G_TYPE_STRING, codec_id, NULL); + gst_structure_set (r_in, "transport-id", G_TYPE_STRING, transport_id, NULL); + /* XXX: mediaType, trackId, sliCount, qpSum */ + + if (gst_structure_get_uint64 (source_stats, "packets-received", &packets)) + gst_structure_set (r_in, "packets-received", G_TYPE_UINT64, packets, + NULL); + if (gst_structure_get_int (source_stats, "packets-lost", &lost)) + gst_structure_set (r_in, "packets-lost", G_TYPE_INT, lost, NULL); + if (gst_structure_get_uint (source_stats, "jitter", &jitter)) + gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE, + CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL); + +/* XXX: RTCReceivedRTPStreamStats double fractionLost; unsigned long packetsDiscarded; unsigned long packetsFailedDecryption; @@ -150,33 +143,89 @@ _get_stats_from_rtp_source_stats (GstWebRTCBin * webrtc, double gapDiscardRate; */ - /* RTCInboundRTPStreamStats */ - gst_structure_set (in, "remote-id", G_TYPE_STRING, r_out_id, NULL); - /* XXX: framesDecoded, lastPacketReceivedTimestamp */ + /* RTCRemoteInboundRTPStreamStats */ + /* XXX: framesDecoded, lastPacketReceivedTimestamp */ - r_in = gst_structure_new_empty (r_in_id); - _set_base_stats (r_in, GST_WEBRTC_STATS_REMOTE_INBOUND_RTP, ts, r_in_id); + out = gst_structure_new_empty (out_id); + _set_base_stats (out, GST_WEBRTC_STATS_OUTBOUND_RTP, ts, out_id); - /* RTCStreamStats */ - gst_structure_set (r_in, "ssrc", G_TYPE_UINT, ssrc, NULL); - gst_structure_set (r_in, "codec-id", G_TYPE_STRING, codec_id, NULL); - gst_structure_set (r_in, "transport-id", G_TYPE_STRING, transport_id, NULL); - /* XXX: mediaType, trackId, sliCount, qpSum */ + /* RTCStreamStats */ + gst_structure_set (out, "ssrc", G_TYPE_UINT, ssrc, NULL); + gst_structure_set (out, "codec-id", G_TYPE_STRING, codec_id, NULL); + gst_structure_set (out, "transport-id", G_TYPE_STRING, transport_id, NULL); + if (gst_structure_get_uint (source_stats, "sent-fir-count", &fir)) + gst_structure_set (out, "fir-count", G_TYPE_UINT, fir, NULL); + if (gst_structure_get_uint (source_stats, "sent-pli-count", &pli)) + gst_structure_set (out, "pli-count", G_TYPE_UINT, pli, NULL); + if (gst_structure_get_uint (source_stats, "sent-nack-count", &nack)) + gst_structure_set (out, "nack-count", G_TYPE_UINT, nack, NULL); + /* XXX: mediaType, trackId, sliCount, qpSum */ - /* RTCReceivedRTPStreamStats */ - if (sent_rb) { - if (gst_structure_get_uint (source_stats, "sent-rb-jitter", &jitter)) - gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE, - CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL); - if (gst_structure_get_int (source_stats, "sent-rb-packetslost", &lost)) - gst_structure_set (r_in, "packets-lost", G_TYPE_INT, lost, NULL); - /* packetsReceived, bytesReceived */ +/* RTCSentRTPStreamStats */ + if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes)) + gst_structure_set (out, "bytes-sent", G_TYPE_UINT64, bytes, NULL); + if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets)) + gst_structure_set (out, "packets-sent", G_TYPE_UINT64, packets, NULL); +/* XXX: + unsigned long packetsDiscardedOnSend; + unsigned long long bytesDiscardedOnSend; +*/ + + /* RTCOutboundRTPStreamStats */ + gst_structure_set (out, "remote-id", G_TYPE_STRING, r_in_id, NULL); +/* XXX: + DOMHighResTimeStamp lastPacketSentTimestamp; + double targetBitrate; + unsigned long framesEncoded; + double totalEncodeTime; + double averageRTCPInterval; +*/ + gst_structure_set (s, out_id, GST_TYPE_STRUCTURE, out, NULL); + gst_structure_set (s, r_in_id, GST_TYPE_STRUCTURE, r_in, NULL); + + gst_structure_free (out); + gst_structure_free (r_in); + + g_free (out_id); + g_free (r_in_id); } else { - /* default values */ - gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE, 0.0, "packets-lost", - G_TYPE_INT, 0, NULL); - } -/* XXX: RTCReceivedRTPStreamStats + GstStructure *in, *r_out; + gchar *r_out_id, *in_id; + gboolean have_rb = FALSE, have_sr = FALSE; + + gst_structure_get (source_stats, "have-rb", G_TYPE_BOOLEAN, &have_rb, + "have-sr", G_TYPE_BOOLEAN, &have_sr, NULL); + + in_id = g_strdup_printf ("rtp-inbound-stream-stats_%u", ssrc); + r_out_id = g_strdup_printf ("rtp-remote-outbound-stream-stats_%u", ssrc); + + in = gst_structure_new_empty (in_id); + _set_base_stats (in, GST_WEBRTC_STATS_INBOUND_RTP, ts, in_id); + + /* RTCStreamStats */ + gst_structure_set (in, "ssrc", G_TYPE_UINT, ssrc, NULL); + gst_structure_set (in, "codec-id", G_TYPE_STRING, codec_id, NULL); + gst_structure_set (in, "transport-id", G_TYPE_STRING, transport_id, NULL); + if (gst_structure_get_uint (source_stats, "recv-fir-count", &fir)) + gst_structure_set (in, "fir-count", G_TYPE_UINT, fir, NULL); + if (gst_structure_get_uint (source_stats, "recv-pli-count", &pli)) + gst_structure_set (in, "pli-count", G_TYPE_UINT, pli, NULL); + if (gst_structure_get_uint (source_stats, "recv-nack-count", &nack)) + gst_structure_set (in, "nack-count", G_TYPE_UINT, nack, NULL); + /* XXX: mediaType, trackId, sliCount, qpSum */ + + /* RTCReceivedRTPStreamStats */ + if (gst_structure_get_uint64 (source_stats, "packets-received", &packets)) + gst_structure_set (in, "packets-received", G_TYPE_UINT64, packets, NULL); + if (gst_structure_get_uint64 (source_stats, "octets-received", &bytes)) + gst_structure_set (in, "bytes-received", G_TYPE_UINT64, bytes, NULL); + if (gst_structure_get_int (source_stats, "packets-lost", &lost)) + gst_structure_set (in, "packets-lost", G_TYPE_INT, lost, NULL); + if (gst_structure_get_uint (source_stats, "jitter", &jitter)) + gst_structure_set (in, "jitter", G_TYPE_DOUBLE, + CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL); +/* + RTCReceivedRTPStreamStats double fractionLost; unsigned long packetsDiscarded; unsigned long packetsFailedDecryption; @@ -191,91 +240,65 @@ _get_stats_from_rtp_source_stats (GstWebRTCBin * webrtc, double gapDiscardRate; */ - /* RTCRemoteInboundRTPStreamStats */ - gst_structure_set (r_in, "local-id", G_TYPE_STRING, out_id, NULL); - if (have_rb) { - guint32 rtt; - if (gst_structure_get_uint (source_stats, "rb-round-trip", &rtt)) { - /* 16.16 fixed point to double */ - double val = - (double) ((rtt & 0xffff0000) >> 16) + ((rtt & 0xffff) / 65536.0); - gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, val, NULL); + /* RTCInboundRTPStreamStats */ + gst_structure_set (in, "remote-id", G_TYPE_STRING, r_out_id, NULL); + /* XXX: framesDecoded, lastPacketReceivedTimestamp */ + + r_out = gst_structure_new_empty (r_out_id); + _set_base_stats (r_out, GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP, ts, r_out_id); + /* RTCStreamStats */ + gst_structure_set (r_out, "ssrc", G_TYPE_UINT, ssrc, NULL); + gst_structure_set (r_out, "codec-id", G_TYPE_STRING, codec_id, NULL); + gst_structure_set (r_out, "transport-id", G_TYPE_STRING, transport_id, + NULL); + if (have_rb) { + guint32 rtt; + if (gst_structure_get_uint (source_stats, "rb-round-trip", &rtt)) { + /* 16.16 fixed point to double */ + double val = FIXED_16_16_TO_DOUBLE (rtt); + gst_structure_set (r_out, "round-trip-time", G_TYPE_DOUBLE, val, NULL); + } + } else { + /* default values */ + gst_structure_set (r_out, "round-trip-time", G_TYPE_DOUBLE, 0.0, NULL); } - } else { - /* default values */ - gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, 0.0, NULL); - } - /* XXX: framesDecoded, lastPacketReceivedTimestamp */ - - out = gst_structure_new_empty (out_id); - _set_base_stats (out, GST_WEBRTC_STATS_OUTBOUND_RTP, ts, out_id); - - /* RTCStreamStats */ - gst_structure_set (out, "ssrc", G_TYPE_UINT, ssrc, NULL); - gst_structure_set (out, "codec-id", G_TYPE_STRING, codec_id, NULL); - gst_structure_set (out, "transport-id", G_TYPE_STRING, transport_id, NULL); - if (gst_structure_get_uint (source_stats, "sent-fir-count", &fir)) - gst_structure_set (out, "fir-count", G_TYPE_UINT, fir, NULL); - if (gst_structure_get_uint (source_stats, "sent-pli-count", &pli)) - gst_structure_set (out, "pli-count", G_TYPE_UINT, pli, NULL); - if (gst_structure_get_uint (source_stats, "sent-nack-count", &nack)) - gst_structure_set (out, "nack-count", G_TYPE_UINT, nack, NULL); - /* XXX: mediaType, trackId, sliCount, qpSum */ + /* XXX: mediaType, trackId, sliCount, qpSum */ /* RTCSentRTPStreamStats */ - if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes)) - gst_structure_set (out, "bytes-sent", G_TYPE_UINT64, bytes, NULL); - if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets)) - gst_structure_set (out, "packets-sent", G_TYPE_UINT64, packets, NULL); + if (have_sr) { + if (gst_structure_get_uint64 (source_stats, "sr-octet-count", &bytes)) + gst_structure_set (r_out, "bytes-sent", G_TYPE_UINT64, bytes, NULL); + if (gst_structure_get_uint64 (source_stats, "sr-packet-count", &packets)) + gst_structure_set (r_out, "packets-sent", G_TYPE_UINT64, packets, NULL); + } /* XXX: unsigned long packetsDiscardedOnSend; unsigned long long bytesDiscardedOnSend; */ - /* RTCOutboundRTPStreamStats */ - gst_structure_set (out, "remote-id", G_TYPE_STRING, r_in_id, NULL); -/* XXX: - DOMHighResTimeStamp lastPacketSentTimestamp; - double targetBitrate; - unsigned long framesEncoded; - double totalEncodeTime; - double averageRTCPInterval; -*/ - - r_out = gst_structure_new_empty (r_out_id); - _set_base_stats (r_out, GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP, ts, r_out_id); - /* RTCStreamStats */ - gst_structure_set (r_out, "ssrc", G_TYPE_UINT, ssrc, NULL); - gst_structure_set (r_out, "codec-id", G_TYPE_STRING, codec_id, NULL); - gst_structure_set (r_out, "transport-id", G_TYPE_STRING, transport_id, NULL); - /* XXX: mediaType, trackId, sliCount, qpSum */ - -/* RTCSentRTPStreamStats */ -/* if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes)) - gst_structure_set (r_out, "bytes-sent", G_TYPE_UINT64, bytes, NULL); - if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets)) - gst_structure_set (r_out, "packets-sent", G_TYPE_UINT64, packets, NULL);*/ -/* XXX: - unsigned long packetsDiscardedOnSend; - unsigned long long bytesDiscardedOnSend; -*/ + if (have_sr) { + guint64 ntptime; + if (gst_structure_get_uint64 (source_stats, "sr-ntptime", &ntptime)) { + /* 16.16 fixed point to double */ + double val = FIXED_32_32_TO_DOUBLE (ntptime); + gst_structure_set (r_out, "remote-timestamp", G_TYPE_DOUBLE, val, NULL); + } + } else { + /* default values */ + gst_structure_set (r_out, "remote-timestamp", G_TYPE_DOUBLE, 0.0, NULL); + } - gst_structure_set (r_out, "local-id", G_TYPE_STRING, in_id, NULL); + gst_structure_set (r_out, "local-id", G_TYPE_STRING, in_id, NULL); - gst_structure_set (s, in_id, GST_TYPE_STRUCTURE, in, NULL); - gst_structure_set (s, out_id, GST_TYPE_STRUCTURE, out, NULL); - gst_structure_set (s, r_in_id, GST_TYPE_STRUCTURE, r_in, NULL); - gst_structure_set (s, r_out_id, GST_TYPE_STRUCTURE, r_out, NULL); + gst_structure_set (s, in_id, GST_TYPE_STRUCTURE, in, NULL); + gst_structure_set (s, r_out_id, GST_TYPE_STRUCTURE, r_out, NULL); - gst_structure_free (in); - gst_structure_free (out); - gst_structure_free (r_in); - gst_structure_free (r_out); + gst_structure_free (in); + gst_structure_free (r_out); - g_free (in_id); - g_free (out_id); - g_free (r_in_id); - g_free (r_out_id); + g_free (in_id); + g_free (r_out_id); + } } /* https://www.w3.org/TR/webrtc-stats/#candidatepair-dict* */ @@ -353,6 +376,7 @@ _get_stats_from_dtls_transport (GstWebRTCBin * webrtc, GstStructure *stats; gchar *id; double ts; + gchar *ice_id; gst_structure_get_double (s, "timestamp", &ts); @@ -395,7 +419,8 @@ _get_stats_from_dtls_transport (GstWebRTCBin * webrtc, gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL); gst_structure_free (stats); - _get_stats_from_ice_transport (webrtc, transport->transport, s); + ice_id = _get_stats_from_ice_transport (webrtc, transport->transport, s); + g_free (ice_id); return id; } @@ -439,16 +464,13 @@ _get_stats_from_transport_channel (GstWebRTCBin * webrtc, for (i = 0; i < source_stats->n_values; i++) { const GstStructure *stats; const GValue *val = g_value_array_get_nth (source_stats, i); - gboolean internal; guint stats_ssrc = 0; stats = gst_value_get_structure (val); - /* skip internal or foreign sources */ - gst_structure_get (stats, - "internal", G_TYPE_BOOLEAN, &internal, - "ssrc", G_TYPE_UINT, &stats_ssrc, NULL); - if (internal || (ssrc && stats_ssrc && ssrc != stats_ssrc)) + /* skip foreign sources */ + gst_structure_get (stats, "ssrc", G_TYPE_UINT, &stats_ssrc, NULL); + if (ssrc && stats_ssrc && ssrc != stats_ssrc) continue; _get_stats_from_rtp_source_stats (webrtc, stats, codec_id, transport_id, s); diff --git a/ext/webrtc/meson.build b/ext/webrtc/meson.build index e9e7c11a3..3e7a5d1d8 100644 --- a/ext/webrtc/meson.build +++ b/ext/webrtc/meson.build @@ -30,4 +30,5 @@ if libnice_dep.found() install_dir : plugins_install_dir, ) pkgconfig.generate(gstwebrtc_plugin, install_dir : plugins_pkgconfig_install_dir) + plugins += [gstwebrtc_plugin] endif diff --git a/ext/webrtc/nicetransport.c b/ext/webrtc/nicetransport.c index 5e7f30e71..bbe910f80 100644 --- a/ext/webrtc/nicetransport.c +++ b/ext/webrtc/nicetransport.c @@ -221,9 +221,7 @@ gst_webrtc_nice_transport_constructed (GObject * object) if (ice->sink) { g_object_set (ice->sink, "agent", agent, "stream", our_stream_id, "component", component, "async", FALSE, "enable-last-sample", FALSE, - NULL); - if (ice->component == GST_WEBRTC_ICE_COMPONENT_RTCP) - g_object_set (ice->sink, "sync", FALSE, NULL); + "sync", FALSE, NULL); } g_object_unref (agent); diff --git a/ext/webrtc/sctptransport.c b/ext/webrtc/sctptransport.c index f5643e9fe..f5a1e9db4 100644 --- a/ext/webrtc/sctptransport.c +++ b/ext/webrtc/sctptransport.c @@ -91,7 +91,8 @@ _sctp_enqueue_task (GstWebRTCSCTPTransport * sctp, SCTPTask func, task->notify = notify; gst_webrtc_bin_enqueue_task (sctp->webrtcbin, - (GstWebRTCBinFunc) _execute_task, task, (GDestroyNotify) _free_task); + (GstWebRTCBinFunc) _execute_task, task, (GDestroyNotify) _free_task, + NULL); } static void @@ -254,8 +255,7 @@ gst_webrtc_sctp_transport_class_init (GstWebRTCSCTPTransportClass * klass) */ gst_webrtc_sctp_transport_signals[ON_RESET_STREAM_SIGNAL] = g_signal_new ("stream-reset", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, G_TYPE_UINT); + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); } static void diff --git a/ext/webrtc/sctptransport.h b/ext/webrtc/sctptransport.h index d5327a77e..212f15eb9 100644 --- a/ext/webrtc/sctptransport.h +++ b/ext/webrtc/sctptransport.h @@ -47,6 +47,7 @@ struct _GstWebRTCSCTPTransport gboolean association_established; + gulong sctpdec_block_id; GstElement *sctpdec; GstElement *sctpenc; diff --git a/ext/webrtc/transportreceivebin.c b/ext/webrtc/transportreceivebin.c index d059fa270..6d38a8325 100644 --- a/ext/webrtc/transportreceivebin.c +++ b/ext/webrtc/transportreceivebin.c @@ -89,8 +89,6 @@ _receive_state_to_string (ReceiveState state) switch (state) { case RECEIVE_STATE_BLOCK: return "block"; - case RECEIVE_STATE_DROP: - return "drop"; case RECEIVE_STATE_PASS: return "pass"; default: @@ -101,46 +99,81 @@ _receive_state_to_string (ReceiveState state) static GstPadProbeReturn pad_block (GstPad * pad, GstPadProbeInfo * info, TransportReceiveBin * receive) { - g_mutex_lock (&receive->pad_block_lock); - while (receive->receive_state == RECEIVE_STATE_BLOCK) { - g_cond_wait (&receive->pad_block_cond, &receive->pad_block_lock); - GST_DEBUG_OBJECT (pad, "probe waited. new state %s", - _receive_state_to_string (receive->receive_state)); - } - - g_mutex_unlock (&receive->pad_block_lock); + /* Drop all events: we don't care about them and don't want to block on + * them. Sticky events would be forwarded again later once we unblock + * and we don't want to forward them here already because that might + * cause a spurious GST_FLOW_FLUSHING */ + if (GST_IS_EVENT (info->data)) + return GST_PAD_PROBE_DROP; + + /* But block on any actual data-flow so we don't accidentally send that + * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything + * to silently stop. + */ + GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data); return GST_PAD_PROBE_OK; } -static GstPadProbeReturn -src_probe_cb (GstPad * pad, GstPadProbeInfo * info, - TransportReceiveBin * receive) -{ - GstPadProbeReturn ret; - - g_mutex_lock (&receive->pad_block_lock); - - g_assert (receive->receive_state != RECEIVE_STATE_BLOCK); - - ret = - receive->receive_state == - RECEIVE_STATE_DROP ? GST_PAD_PROBE_DROP : GST_PAD_PROBE_OK; - - g_mutex_unlock (&receive->pad_block_lock); - - return ret; -} - void transport_receive_bin_set_receive_state (TransportReceiveBin * receive, ReceiveState state) { + g_mutex_lock (&receive->pad_block_lock); + if (receive->receive_state != state) { + GST_DEBUG_OBJECT (receive, "changing receive state to %s", + _receive_state_to_string (state)); + } + + if (state == RECEIVE_STATE_PASS) { + if (receive->rtp_block) + _free_pad_block (receive->rtp_block); + receive->rtp_block = NULL; + + if (receive->rtcp_block) + _free_pad_block (receive->rtcp_block); + receive->rtcp_block = NULL; + } else { + g_assert (state == RECEIVE_STATE_BLOCK); + if (receive->rtp_block == NULL) { + GstWebRTCDTLSTransport *transport; + GstElement *dtlssrtpdec; + GstPad *pad, *peer_pad; + + if (receive->stream) { + transport = receive->stream->transport; + dtlssrtpdec = transport->dtlssrtpdec; + pad = gst_element_get_static_pad (dtlssrtpdec, "sink"); + peer_pad = gst_pad_get_peer (pad); + receive->rtp_block = + _create_pad_block (GST_ELEMENT (receive), peer_pad, 0, NULL, NULL); + receive->rtp_block->block_id = + gst_pad_add_probe (peer_pad, + GST_PAD_PROBE_TYPE_BLOCK | + GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, + (GstPadProbeCallback) pad_block, receive, NULL); + gst_object_unref (peer_pad); + gst_object_unref (pad); + + transport = receive->stream->rtcp_transport; + dtlssrtpdec = transport->dtlssrtpdec; + pad = gst_element_get_static_pad (dtlssrtpdec, "sink"); + peer_pad = gst_pad_get_peer (pad); + receive->rtcp_block = + _create_pad_block (GST_ELEMENT (receive), peer_pad, 0, NULL, NULL); + receive->rtcp_block->block_id = + gst_pad_add_probe (peer_pad, + GST_PAD_PROBE_TYPE_BLOCK | + GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, + (GstPadProbeCallback) pad_block, receive, NULL); + gst_object_unref (peer_pad); + gst_object_unref (pad); + } + } + } + receive->receive_state = state; - GST_DEBUG_OBJECT (receive, "changing receive state to %s", - _receive_state_to_string (state)); - g_cond_signal (&receive->pad_block_cond); g_mutex_unlock (&receive->pad_block_lock); } @@ -187,7 +220,6 @@ transport_receive_bin_finalize (GObject * object) TransportReceiveBin *receive = TRANSPORT_RECEIVE_BIN (object); g_mutex_clear (&receive->pad_block_lock); - g_cond_clear (&receive->pad_block_cond); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -205,37 +237,12 @@ transport_receive_bin_change_state (GstElement * element, switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY:{ - GstWebRTCDTLSTransport *transport; - GstElement *elem, *dtlssrtpdec; - GstPad *pad; - - transport = receive->stream->transport; - dtlssrtpdec = transport->dtlssrtpdec; - pad = gst_element_get_static_pad (dtlssrtpdec, "sink"); - receive->rtp_block = - _create_pad_block (GST_ELEMENT (receive), pad, 0, NULL, NULL); - receive->rtp_block->block_id = - gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_ALL_BOTH, - (GstPadProbeCallback) pad_block, receive, NULL); - gst_object_unref (pad); - - receive->rtp_src_probe_id = gst_pad_add_probe (receive->rtp_src, - GST_PAD_PROBE_TYPE_ALL_BOTH, (GstPadProbeCallback) src_probe_cb, - receive, NULL); - - transport = receive->stream->rtcp_transport; - dtlssrtpdec = transport->dtlssrtpdec; - pad = gst_element_get_static_pad (dtlssrtpdec, "sink"); - receive->rtcp_block = - _create_pad_block (GST_ELEMENT (receive), pad, 0, NULL, NULL); - receive->rtcp_block->block_id = - gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_ALL_BOTH, - (GstPadProbeCallback) pad_block, receive, NULL); - gst_object_unref (pad); - - receive->rtcp_src_probe_id = gst_pad_add_probe (receive->rtcp_src, - GST_PAD_PROBE_TYPE_ALL_BOTH, (GstPadProbeCallback) src_probe_cb, - receive, NULL); + GstElement *elem; + + /* We want to start blocked, unless someone already switched us + * to PASS mode. receive_state is set to BLOCKED in _init(), + * so set up blocks with whatever the mode is now. */ + transport_receive_bin_set_receive_state (receive, receive->receive_state); /* XXX: because nice needs the nicesrc internal main loop running in order * correctly STUN... */ @@ -271,18 +278,10 @@ transport_receive_bin_change_state (GstElement * element, _free_pad_block (receive->rtp_block); receive->rtp_block = NULL; - if (receive->rtp_src_probe_id) - gst_pad_remove_probe (receive->rtp_src, receive->rtp_src_probe_id); - receive->rtp_src_probe_id = 0; - if (receive->rtcp_block) _free_pad_block (receive->rtcp_block); receive->rtcp_block = NULL; - if (receive->rtcp_src_probe_id) - gst_pad_remove_probe (receive->rtcp_src, receive->rtcp_src_probe_id); - receive->rtcp_src_probe_id = 0; - break; } default: @@ -440,7 +439,7 @@ transport_receive_bin_class_init (TransportReceiveBinClass * klass) g_object_class_install_property (gobject_class, PROP_STREAM, g_param_spec_object ("stream", "Stream", - "The TransportStream for this receiveing bin", + "The TransportStream for this receiving bin", transport_stream_get_type (), G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } @@ -448,6 +447,6 @@ transport_receive_bin_class_init (TransportReceiveBinClass * klass) static void transport_receive_bin_init (TransportReceiveBin * receive) { + receive->receive_state = RECEIVE_STATE_BLOCK; g_mutex_init (&receive->pad_block_lock); - g_cond_init (&receive->pad_block_cond); } diff --git a/ext/webrtc/transportreceivebin.h b/ext/webrtc/transportreceivebin.h index a4c187064..50449e327 100644 --- a/ext/webrtc/transportreceivebin.h +++ b/ext/webrtc/transportreceivebin.h @@ -33,8 +33,8 @@ GType transport_receive_bin_get_type(void); typedef enum { + RECEIVE_STATE_UNSET = 0, RECEIVE_STATE_BLOCK = 1, - RECEIVE_STATE_DROP, RECEIVE_STATE_PASS, } ReceiveState; @@ -43,7 +43,6 @@ struct _TransportReceiveBin GstBin parent; TransportStream *stream; /* parent transport stream */ - gboolean rtcp_mux; GstPad *rtp_src; gulong rtp_src_probe_id; @@ -52,7 +51,6 @@ struct _TransportReceiveBin struct pad_block *rtp_block; struct pad_block *rtcp_block; GMutex pad_block_lock; - GCond pad_block_cond; ReceiveState receive_state; }; diff --git a/ext/webrtc/transportsendbin.c b/ext/webrtc/transportsendbin.c index 36522d336..dc5c1ff0e 100644 --- a/ext/webrtc/transportsendbin.c +++ b/ext/webrtc/transportsendbin.c @@ -81,7 +81,6 @@ enum #define TSB_UNLOCK(tsb) (g_mutex_unlock (TSB_GET_LOCK(tsb))) static void cleanup_blocks (TransportSendBin * send); -static void tsb_remove_probe (struct pad_block *block); static void _set_rtcp_mux (TransportSendBin * send, gboolean rtcp_mux) @@ -147,6 +146,17 @@ transport_send_bin_get_property (GObject * object, guint prop_id, static GstPadProbeReturn pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer unused) { + /* Drop all events: we don't care about them and don't want to block on + * them. Sticky events would be forwarded again later once we unblock + * and we don't want to forward them here already because that might + * cause a spurious GST_FLOW_FLUSHING */ + if (GST_IS_EVENT (info->data)) + return GST_PAD_PROBE_DROP; + + /* But block on any actual data-flow so we don't accidentally send that + * to a pad that is not ready yet, causing GST_FLOW_FLUSHING and everything + * to silently stop. + */ GST_LOG_OBJECT (pad, "blocking pad with data %" GST_PTR_FORMAT, info->data); return GST_PAD_PROBE_OK; @@ -166,23 +176,13 @@ block_peer_pad (GstElement * elem, const gchar * pad_name) peer = gst_pad_get_peer (pad); block = _create_pad_block (elem, peer, 0, NULL, NULL); block->block_id = gst_pad_add_probe (peer, - GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER | - GST_PAD_PROBE_TYPE_BUFFER_LIST, (GstPadProbeCallback) pad_block, NULL, - NULL); + GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, + (GstPadProbeCallback) pad_block, NULL, NULL); gst_object_unref (pad); gst_object_unref (peer); return block; } -static void -tsb_remove_probe (struct pad_block *block) -{ - if (block && block->block_id) { - gst_pad_remove_probe (block->pad, block->block_id); - block->block_id = 0; - } -} - static GstStateChangeReturn transport_send_bin_change_state (GstElement * element, GstStateChange transition) @@ -247,12 +247,7 @@ transport_send_bin_change_state (GstElement * element, * if they still exist, without accidentally feeding data to the * dtlssrtpenc elements */ TSB_LOCK (send); - tsb_remove_probe (send->rtp_ctx.rtp_block); - tsb_remove_probe (send->rtp_ctx.rtcp_block); - tsb_remove_probe (send->rtp_ctx.nice_block); - - tsb_remove_probe (send->rtcp_ctx.rtcp_block); - tsb_remove_probe (send->rtcp_ctx.nice_block); + cleanup_blocks (send); TSB_UNLOCK (send); break; } @@ -516,6 +511,85 @@ transport_send_bin_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static gboolean +gst_transport_send_bin_element_query (GstElement * element, GstQuery * query) +{ + gboolean ret = TRUE; + GstClockTime min_latency; + + GST_LOG_OBJECT (element, "got query %s", GST_QUERY_TYPE_NAME (query)); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + /* when latency is queried, use the result to configure our + * own latency internally, piggybacking off the global + * latency configuration sequence. */ + GST_DEBUG_OBJECT (element, "handling latency query"); + + /* Call the parent query handler to actually get the query + * sent upstream */ + ret = + GST_ELEMENT_CLASS (transport_send_bin_parent_class)->query + (GST_ELEMENT (element), query); + if (!ret) + break; + + gst_query_parse_latency (query, NULL, &min_latency, NULL); + + GST_DEBUG_OBJECT (element, + "got min latency %" GST_TIME_FORMAT, GST_TIME_ARGS (min_latency)); + + /* configure latency on elements */ + /* Call the parent event handler, because our sub-class handler + * will drop the LATENCY event. We also don't need to that + * the latency configuration is valid (min < max), because + * the pipeline will do it when checking the query results */ + if (GST_ELEMENT_CLASS (transport_send_bin_parent_class)->send_event + (GST_ELEMENT (element), gst_event_new_latency (min_latency))) { + GST_INFO_OBJECT (element, "configured latency of %" GST_TIME_FORMAT, + GST_TIME_ARGS (min_latency)); + } else { + GST_WARNING_OBJECT (element, + "did not really configure latency of %" GST_TIME_FORMAT, + GST_TIME_ARGS (min_latency)); + } + + break; + default: + ret = + GST_ELEMENT_CLASS (transport_send_bin_parent_class)->query + (GST_ELEMENT (element), query); + break; + } + + return ret; +} + +static gboolean +gst_transport_send_bin_element_event (GstElement * element, GstEvent * event) +{ + gboolean ret = TRUE; + + GST_LOG_OBJECT (element, "got event %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_LATENCY: + /* Ignore the pipeline configured latency, we choose our own + * instead when the latency query happens, so that sending + * isn't affected by other parts of the pipeline */ + GST_DEBUG_OBJECT (element, "Ignoring latency event from parent"); + gst_event_unref (event); + break; + default: + ret = + GST_ELEMENT_CLASS (transport_send_bin_parent_class)->send_event + (GST_ELEMENT (element), event); + break; + } + + return ret; +} + static void transport_send_bin_class_init (TransportSendBinClass * klass) { @@ -540,6 +614,9 @@ transport_send_bin_class_init (TransportSendBinClass * klass) gobject_class->set_property = transport_send_bin_set_property; gobject_class->finalize = transport_send_bin_finalize; + element_class->send_event = gst_transport_send_bin_element_event; + element_class->query = gst_transport_send_bin_element_query; + g_object_class_install_property (gobject_class, PROP_STREAM, g_param_spec_object ("stream", "Stream", diff --git a/ext/webrtc/transportstream.c b/ext/webrtc/transportstream.c index 01fa2dc91..01261ae1b 100644 --- a/ext/webrtc/transportstream.c +++ b/ext/webrtc/transportstream.c @@ -75,6 +75,36 @@ transport_stream_get_pt (TransportStream * stream, const gchar * encoding_name) return ret; } +int * +transport_stream_get_all_pt (TransportStream * stream, + const gchar * encoding_name, gsize * pt_len) +{ + guint i; + gsize ret_i = 0; + gsize ret_size = 8; + int *ret = NULL; + + for (i = 0; i < stream->ptmap->len; i++) { + PtMapItem *item = &g_array_index (stream->ptmap, PtMapItem, i); + if (!gst_caps_is_empty (item->caps)) { + GstStructure *s = gst_caps_get_structure (item->caps, 0); + if (!g_strcmp0 (gst_structure_get_string (s, "encoding-name"), + encoding_name)) { + if (!ret) + ret = g_new0 (int, ret_size); + if (ret_i >= ret_size) { + ret_size *= 2; + ret = g_realloc_n (ret, ret_size, sizeof (int)); + } + ret[ret_i++] = item->pt; + } + } + } + + *pt_len = ret_i; + return ret; +} + static void transport_stream_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -152,6 +182,14 @@ transport_stream_dispose (GObject * object) gst_object_unref (stream->rtcp_transport); stream->rtcp_transport = NULL; + if (stream->rtxsend) + gst_object_unref (stream->rtxsend); + stream->rtxsend = NULL; + + if (stream->rtxreceive) + gst_object_unref (stream->rtxreceive); + stream->rtxreceive = NULL; + GST_OBJECT_PARENT (object) = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); diff --git a/ext/webrtc/transportstream.h b/ext/webrtc/transportstream.h index 8b90a946a..174d93e90 100644 --- a/ext/webrtc/transportstream.h +++ b/ext/webrtc/transportstream.h @@ -52,6 +52,7 @@ struct _TransportStream gboolean rtcp_mux; gboolean rtcp_rsize; gboolean dtls_client; + gboolean active; /* TRUE if any mline in the bundle/transport is active */ TransportSendBin *send_bin; /* bin containing all the sending transport elements */ TransportReceiveBin *receive_bin; /* bin containing all the receiving transport elements */ GstWebRTCICEStream *stream; @@ -61,6 +62,10 @@ struct _TransportStream GArray *ptmap; /* array of PtMapItem's */ GArray *remote_ssrcmap; /* array of SsrcMapItem's */ + gboolean output_connected; /* whether receive bin is connected to rtpbin */ + + GstElement *rtxsend; + GstElement *rtxreceive; }; struct _TransportStreamClass @@ -72,6 +77,9 @@ TransportStream * transport_stream_new (GstWebRTCBin * webrtc, guint session_id); int transport_stream_get_pt (TransportStream * stream, const gchar * encoding_name); +int * transport_stream_get_all_pt (TransportStream * stream, + const gchar * encoding_name, + gsize * pt_len); GstCaps * transport_stream_get_caps_for_pt (TransportStream * stream, guint pt); diff --git a/ext/webrtc/utils.c b/ext/webrtc/utils.c index ac050bf01..044d58322 100644 --- a/ext/webrtc/utils.c +++ b/ext/webrtc/utils.c @@ -21,6 +21,8 @@ # include "config.h" #endif +#include + #include "utils.h" #include "gstwebrtcbin.h" @@ -53,28 +55,59 @@ _find_pad_template (GstElement * element, GstPadDirection direction, } GstSDPMessage * -_get_latest_sdp (GstWebRTCBin * webrtc) +_get_latest_offer (GstWebRTCBin * webrtc) { if (webrtc->current_local_description && - webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) { + webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) { return webrtc->current_local_description->sdp; } if (webrtc->current_remote_description && - webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) { + webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_OFFER) { return webrtc->current_remote_description->sdp; } + + return NULL; +} + +GstSDPMessage * +_get_latest_answer (GstWebRTCBin * webrtc) +{ if (webrtc->current_local_description && - webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_OFFER) { + webrtc->current_local_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) { return webrtc->current_local_description->sdp; } if (webrtc->current_remote_description && - webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_OFFER) { + webrtc->current_remote_description->type == GST_WEBRTC_SDP_TYPE_ANSWER) { return webrtc->current_remote_description->sdp; } return NULL; } +GstSDPMessage * +_get_latest_sdp (GstWebRTCBin * webrtc) +{ + GstSDPMessage *ret = NULL; + + if ((ret = _get_latest_answer (webrtc))) + return ret; + if ((ret = _get_latest_offer (webrtc))) + return ret; + + return NULL; +} + +GstSDPMessage * +_get_latest_self_generated_sdp (GstWebRTCBin * webrtc) +{ + if (webrtc->priv->last_generated_answer) + return webrtc->priv->last_generated_answer->sdp; + if (webrtc->priv->last_generated_offer) + return webrtc->priv->last_generated_offer->sdp; + + return NULL; +} + struct pad_block * _create_pad_block (GstElement * element, GstPad * pad, gulong block_id, gpointer user_data, GDestroyNotify notify) @@ -142,3 +175,33 @@ _g_checksum_to_webrtc_string (GChecksumType type) return NULL; } } + +GstCaps * +_rtp_caps_from_media (const GstSDPMedia * media) +{ + GstCaps *ret; + int i, j; + + ret = gst_caps_new_empty (); + for (i = 0; i < gst_sdp_media_formats_len (media); i++) { + guint pt = atoi (gst_sdp_media_get_format (media, i)); + GstCaps *caps; + + caps = gst_sdp_media_get_caps_from_media (media, pt); + if (!caps) + continue; + + /* gst_sdp_media_get_caps_from_media() produces caps with name + * "application/x-unknown" which will fail intersection with + * "application/x-rtp" caps so mangle the returns caps to have the + * correct name here */ + for (j = 0; j < gst_caps_get_size (caps); j++) { + GstStructure *s = gst_caps_get_structure (caps, j); + gst_structure_set_name (s, "application/x-rtp"); + } + + gst_caps_append (ret, caps); + } + + return ret; +} diff --git a/ext/webrtc/utils.h b/ext/webrtc/utils.h index 847264730..ab4d58e87 100644 --- a/ext/webrtc/utils.h +++ b/ext/webrtc/utils.h @@ -39,6 +39,7 @@ typedef enum GST_WEBRTC_BIN_ERROR_FINGERPRINT, GST_WEBRTC_BIN_ERROR_SCTP_FAILURE, GST_WEBRTC_BIN_ERROR_DATA_CHANNEL_FAILURE, + GST_WEBRTC_BIN_ERROR_CLOSED, } GstWebRTCError; GstPadTemplate * _find_pad_template (GstElement * element, @@ -47,6 +48,9 @@ GstPadTemplate * _find_pad_template (GstElement * element, const gchar * name); GstSDPMessage * _get_latest_sdp (GstWebRTCBin * webrtc); +GstSDPMessage * _get_latest_offer (GstWebRTCBin * webrtc); +GstSDPMessage * _get_latest_answer (GstWebRTCBin * webrtc); +GstSDPMessage * _get_latest_self_generated_sdp (GstWebRTCBin * webrtc); GstWebRTCICEStream * _find_ice_stream_for_session (GstWebRTCBin * webrtc, guint session_id); @@ -74,6 +78,8 @@ G_GNUC_INTERNAL gchar * _enum_value_to_string (GType type, guint value); G_GNUC_INTERNAL const gchar * _g_checksum_to_webrtc_string (GChecksumType type); +G_GNUC_INTERNAL +GstCaps * _rtp_caps_from_media (const GstSDPMedia * media); G_END_DECLS diff --git a/ext/webrtc/webrtcdatachannel.c b/ext/webrtc/webrtcdatachannel.c index 7aee40379..fde12613c 100644 --- a/ext/webrtc/webrtcdatachannel.c +++ b/ext/webrtc/webrtcdatachannel.c @@ -23,7 +23,7 @@ * @title: GstWebRTCDataChannel * @see_also: #GstWebRTCRTPTransceiver * - * http://w3c.github.io/webrtc-pc/#dom-rtcsctptransport + * */ #ifdef HAVE_CONFIG_H @@ -41,46 +41,14 @@ #include "gstwebrtcbin.h" #include "utils.h" -#define GST_CAT_DEFAULT gst_webrtc_data_channel_debug +#define GST_CAT_DEFAULT webrtc_data_channel_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); -#define gst_webrtc_data_channel_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstWebRTCDataChannel, gst_webrtc_data_channel, - GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_data_channel_debug, - "webrtcdatachannel", 0, "webrtcdatachannel");); - -enum -{ - SIGNAL_0, - SIGNAL_ON_OPEN, - SIGNAL_ON_CLOSE, - SIGNAL_ON_ERROR, - SIGNAL_ON_MESSAGE_DATA, - SIGNAL_ON_MESSAGE_STRING, - SIGNAL_ON_BUFFERED_AMOUNT_LOW, - SIGNAL_SEND_DATA, - SIGNAL_SEND_STRING, - SIGNAL_CLOSE, - LAST_SIGNAL, -}; - -enum -{ - PROP_0, - PROP_LABEL, - PROP_ORDERED, - PROP_MAX_PACKET_LIFETIME, - PROP_MAX_RETRANSMITS, - PROP_PROTOCOL, - PROP_NEGOTIATED, - PROP_ID, - PROP_PRIORITY, - PROP_READY_STATE, - PROP_BUFFERED_AMOUNT, - PROP_BUFFERED_AMOUNT_LOW_THRESHOLD, -}; - -static guint gst_webrtc_data_channel_signals[LAST_SIGNAL] = { 0 }; +#define webrtc_data_channel_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (WebRTCDataChannel, webrtc_data_channel, + GST_TYPE_WEBRTC_DATA_CHANNEL, + GST_DEBUG_CATEGORY_INIT (webrtc_data_channel_debug, "webrtcdatachannel", 0, + "webrtcdatachannel");); typedef enum { @@ -139,11 +107,11 @@ priority_uint_to_type (guint16 val) } static GstBuffer * -construct_open_packet (GstWebRTCDataChannel * channel) +construct_open_packet (WebRTCDataChannel * channel) { GstByteWriter w; - gsize label_len = strlen (channel->label); - gsize proto_len = strlen (channel->protocol); + gsize label_len = strlen (channel->parent.label); + gsize proto_len = strlen (channel->parent.protocol); gsize size = 12 + label_len + proto_len; DataChannelReliabilityType reliability = 0; guint32 reliability_param = 0; @@ -175,18 +143,18 @@ construct_open_packet (GstWebRTCDataChannel * channel) if (!gst_byte_writer_put_uint8 (&w, (guint8) CHANNEL_MESSAGE_OPEN)) g_return_val_if_reached (NULL); - if (!channel->ordered) + if (!channel->parent.ordered) reliability |= 0x80; - if (channel->max_retransmits != -1) { + if (channel->parent.max_retransmits != -1) { reliability |= 0x01; - reliability_param = channel->max_retransmits; + reliability_param = channel->parent.max_retransmits; } - if (channel->max_packet_lifetime != -1) { + if (channel->parent.max_packet_lifetime != -1) { reliability |= 0x02; - reliability_param = channel->max_packet_lifetime; + reliability_param = channel->parent.max_packet_lifetime; } - priority = priority_type_to_uint (channel->priority); + priority = priority_type_to_uint (channel->parent.priority); if (!gst_byte_writer_put_uint8 (&w, (guint8) reliability)) g_return_val_if_reached (NULL); @@ -198,9 +166,11 @@ construct_open_packet (GstWebRTCDataChannel * channel) g_return_val_if_reached (NULL); if (!gst_byte_writer_put_uint16_be (&w, (guint16) proto_len)) g_return_val_if_reached (NULL); - if (!gst_byte_writer_put_data (&w, (guint8 *) channel->label, label_len)) + if (!gst_byte_writer_put_data (&w, (guint8 *) channel->parent.label, + label_len)) g_return_val_if_reached (NULL); - if (!gst_byte_writer_put_data (&w, (guint8 *) channel->protocol, proto_len)) + if (!gst_byte_writer_put_data (&w, (guint8 *) channel->parent.protocol, + proto_len)) g_return_val_if_reached (NULL); buf = gst_byte_writer_reset_and_get_buffer (&w); @@ -213,7 +183,7 @@ construct_open_packet (GstWebRTCDataChannel * channel) } static GstBuffer * -construct_ack_packet (GstWebRTCDataChannel * channel) +construct_ack_packet (WebRTCDataChannel * channel) { GstByteWriter w; GstBuffer *buf; @@ -269,7 +239,7 @@ _free_task (struct task *task) } static void -_channel_enqueue_task (GstWebRTCDataChannel * channel, ChannelTask func, +_channel_enqueue_task (WebRTCDataChannel * channel, ChannelTask func, gpointer user_data, GDestroyNotify notify) { struct task *task = g_new0 (struct task, 1); @@ -280,13 +250,14 @@ _channel_enqueue_task (GstWebRTCDataChannel * channel, ChannelTask func, task->notify = notify; gst_webrtc_bin_enqueue_task (channel->webrtcbin, - (GstWebRTCBinFunc) _execute_task, task, (GDestroyNotify) _free_task); + (GstWebRTCBinFunc) _execute_task, task, (GDestroyNotify) _free_task, + NULL); } static void -_channel_store_error (GstWebRTCDataChannel * channel, GError * error) +_channel_store_error (WebRTCDataChannel * channel, GError * error) { - GST_OBJECT_LOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); if (error) { GST_WARNING_OBJECT (channel, "Error: %s", error ? error->message : "Unknown"); @@ -295,76 +266,34 @@ _channel_store_error (GstWebRTCDataChannel * channel, GError * error) else g_clear_error (&error); } - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); } static void -_maybe_emit_on_error (GstWebRTCDataChannel * channel, GError * error) +_emit_on_open (WebRTCDataChannel * channel, gpointer user_data) { - if (error) { - GST_WARNING_OBJECT (channel, "error thrown"); - g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_ERROR], 0, - error); - } + gst_webrtc_data_channel_on_open (GST_WEBRTC_DATA_CHANNEL (channel)); } static void -_emit_on_open (GstWebRTCDataChannel * channel, gpointer user_data) -{ - GST_OBJECT_LOCK (channel); - if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING || - channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) { - GST_OBJECT_UNLOCK (channel); - return; - } - - if (channel->ready_state != GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) { - channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_OPEN; - GST_OBJECT_UNLOCK (channel); - g_object_notify (G_OBJECT (channel), "ready-state"); - - GST_INFO_OBJECT (channel, "We are open and ready for data!"); - g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_OPEN], 0, - NULL); - } else { - GST_OBJECT_UNLOCK (channel); - } -} - -static void -_transport_closed_unlocked (GstWebRTCDataChannel * channel) +_transport_closed (WebRTCDataChannel * channel) { GError *error; - if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) - return; - - channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED; - + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); error = channel->stored_error; channel->stored_error = NULL; - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); - g_object_notify (G_OBJECT (channel), "ready-state"); - GST_INFO_OBJECT (channel, "We are closed for data"); - - _maybe_emit_on_error (channel, error); - - g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_CLOSE], 0, - NULL); - GST_OBJECT_LOCK (channel); -} - -static void -_transport_closed (GstWebRTCDataChannel * channel, gpointer user_data) -{ - GST_OBJECT_LOCK (channel); - _transport_closed_unlocked (channel); - GST_OBJECT_UNLOCK (channel); + if (error) { + gst_webrtc_data_channel_on_error (GST_WEBRTC_DATA_CHANNEL (channel), error); + g_clear_error (&error); + } + gst_webrtc_data_channel_on_close (GST_WEBRTC_DATA_CHANNEL (channel)); } static void -_close_sctp_stream (GstWebRTCDataChannel * channel, gpointer user_data) +_close_sctp_stream (WebRTCDataChannel * channel, gpointer user_data) { GstPad *pad, *peer; @@ -382,53 +311,55 @@ _close_sctp_stream (GstWebRTCDataChannel * channel, gpointer user_data) gst_object_unref (peer); } - _transport_closed (channel, NULL); + _transport_closed (channel); } static void -_close_procedure (GstWebRTCDataChannel * channel, gpointer user_data) +_close_procedure (WebRTCDataChannel * channel, gpointer user_data) { /* https://www.w3.org/TR/webrtc/#data-transport-closing-procedure */ - GST_OBJECT_LOCK (channel); - if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED - || channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING) { - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + if (channel->parent.ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED + || channel->parent.ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING) { + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); return; } - channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING; - GST_OBJECT_UNLOCK (channel); + channel->parent.ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING; + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); g_object_notify (G_OBJECT (channel), "ready-state"); - GST_OBJECT_LOCK (channel); - if (channel->buffered_amount <= 0) { + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + if (channel->parent.buffered_amount <= 0) { _channel_enqueue_task (channel, (ChannelTask) _close_sctp_stream, NULL, NULL); } - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); } static void _on_sctp_reset_stream (GstWebRTCSCTPTransport * sctp, guint stream_id, - GstWebRTCDataChannel * channel) + WebRTCDataChannel * channel) { - if (channel->id == stream_id) + if (channel->parent.id == stream_id) _channel_enqueue_task (channel, (ChannelTask) _transport_closed, GUINT_TO_POINTER (stream_id), NULL); } static void -gst_webrtc_data_channel_close (GstWebRTCDataChannel * channel) +webrtc_data_channel_close (GstWebRTCDataChannel * channel) { - _close_procedure (channel, NULL); + _close_procedure (WEBRTC_DATA_CHANNEL (channel), NULL); } static GstFlowReturn -_parse_control_packet (GstWebRTCDataChannel * channel, guint8 * data, +_parse_control_packet (WebRTCDataChannel * channel, guint8 * data, gsize size, GError ** error) { GstByteReader r; guint8 message_type; + gchar *label = NULL; + gchar *proto = NULL; if (!data) g_return_val_if_reached (GST_FLOW_ERROR); @@ -449,13 +380,12 @@ _parse_control_packet (GstWebRTCDataChannel * channel, guint8 * data, guint32 reliability_param; guint16 priority, label_len, proto_len; const guint8 *src; - gchar *label, *proto; GstBuffer *buffer; GstFlowReturn ret; GST_INFO_OBJECT (channel, "Received channel open"); - if (channel->negotiated) { + if (channel->parent.negotiated) { g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_DATA_CHANNEL_FAILURE, "Data channel was signalled as negotiated already"); @@ -488,41 +418,46 @@ _parse_control_packet (GstWebRTCDataChannel * channel, guint8 * data, memcpy (proto, src, proto_len); proto[proto_len] = '\0'; - channel->label = label; - channel->protocol = proto; - channel->priority = priority_uint_to_type (priority); - channel->ordered = !(reliability & 0x80); + g_free (channel->parent.label); + channel->parent.label = label; + g_free (channel->parent.protocol); + channel->parent.protocol = proto; + channel->parent.priority = priority_uint_to_type (priority); + channel->parent.ordered = !(reliability & 0x80); if (reliability & 0x01) { - channel->max_retransmits = reliability_param; - channel->max_packet_lifetime = -1; + channel->parent.max_retransmits = reliability_param; + channel->parent.max_packet_lifetime = -1; } else if (reliability & 0x02) { - channel->max_retransmits = -1; - channel->max_packet_lifetime = reliability_param; + channel->parent.max_retransmits = -1; + channel->parent.max_packet_lifetime = reliability_param; } else { - channel->max_retransmits = -1; - channel->max_packet_lifetime = -1; + channel->parent.max_retransmits = -1; + channel->parent.max_packet_lifetime = -1; } channel->opened = TRUE; GST_INFO_OBJECT (channel, "Received channel open for SCTP stream %i " - "label %s protocol %s ordered %s", channel->id, channel->label, - channel->protocol, channel->ordered ? "true" : "false"); + "label %s protocol %s ordered %s", channel->parent.id, + channel->parent.label, channel->parent.protocol, + channel->parent.ordered ? "true" : "false"); _channel_enqueue_task (channel, (ChannelTask) _emit_on_open, NULL, NULL); GST_INFO_OBJECT (channel, "Sending channel ack"); buffer = construct_ack_packet (channel); - GST_OBJECT_LOCK (channel); - channel->buffered_amount += gst_buffer_get_size (buffer); - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + channel->parent.buffered_amount += gst_buffer_get_size (buffer); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); ret = gst_app_src_push_buffer (GST_APP_SRC (channel->appsrc), buffer); if (ret != GST_FLOW_OK) { g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_DATA_CHANNEL_FAILURE, "Could not send ack packet"); + return ret; } + return ret; } else { g_set_error (error, GST_WEBRTC_BIN_ERROR, @@ -533,6 +468,8 @@ _parse_control_packet (GstWebRTCDataChannel * channel, guint8 * data, parse_error: { + g_free (label); + g_free (proto); g_set_error (error, GST_WEBRTC_BIN_ERROR, GST_WEBRTC_BIN_ERROR_DATA_CHANNEL_FAILURE, "Failed to parse packet"); g_return_val_if_reached (GST_FLOW_ERROR); @@ -559,23 +496,21 @@ buffer_unmap_and_unref (struct map_info *info) } static void -_emit_have_data (GstWebRTCDataChannel * channel, GBytes * data) +_emit_have_data (WebRTCDataChannel * channel, GBytes * data) { - GST_LOG_OBJECT (channel, "Have data %p", data); - g_signal_emit (channel, - gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_DATA], 0, data); + gst_webrtc_data_channel_on_message_data (GST_WEBRTC_DATA_CHANNEL (channel), + data); } static void _emit_have_string (GstWebRTCDataChannel * channel, gchar * str) { - GST_LOG_OBJECT (channel, "Have string %p", str); - g_signal_emit (channel, - gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_STRING], 0, str); + gst_webrtc_data_channel_on_message_string (GST_WEBRTC_DATA_CHANNEL (channel), + str); } static GstFlowReturn -_data_channel_have_sample (GstWebRTCDataChannel * channel, GstSample * sample, +_data_channel_have_sample (WebRTCDataChannel * channel, GstSample * sample, GError ** error) { GstSctpReceiveMeta *receive; @@ -610,6 +545,7 @@ _data_channel_have_sample (GstWebRTCDataChannel * channel, GstSample * sample, ret = GST_FLOW_ERROR; } else { ret = _parse_control_packet (channel, info.data, info.size, error); + gst_buffer_unmap (buffer, &info); } break; } @@ -625,6 +561,7 @@ _data_channel_have_sample (GstWebRTCDataChannel * channel, GstSample * sample, gchar *str = g_strndup ((gchar *) info.data, info.size); _channel_enqueue_task (channel, (ChannelTask) _emit_have_string, str, g_free); + gst_buffer_unmap (buffer, &info); } break; } @@ -667,7 +604,7 @@ _data_channel_have_sample (GstWebRTCDataChannel * channel, GstSample * sample, static GstFlowReturn on_sink_preroll (GstAppSink * sink, gpointer user_data) { - GstWebRTCDataChannel *channel = user_data; + WebRTCDataChannel *channel = user_data; GstSample *sample = gst_app_sink_pull_preroll (sink); GstFlowReturn ret; @@ -692,7 +629,7 @@ on_sink_preroll (GstAppSink * sink, gpointer user_data) static GstFlowReturn on_sink_sample (GstAppSink * sink, gpointer user_data) { - GstWebRTCDataChannel *channel = user_data; + WebRTCDataChannel *channel = user_data; GstSample *sample = gst_app_sink_pull_sample (sink); GstFlowReturn ret; GError *error = NULL; @@ -723,23 +660,24 @@ static GstAppSinkCallbacks sink_callbacks = { }; void -gst_webrtc_data_channel_start_negotiation (GstWebRTCDataChannel * channel) +webrtc_data_channel_start_negotiation (WebRTCDataChannel * channel) { GstBuffer *buffer; - g_return_if_fail (!channel->negotiated); - g_return_if_fail (channel->id != -1); + g_return_if_fail (!channel->parent.negotiated); + g_return_if_fail (channel->parent.id != -1); g_return_if_fail (channel->sctp_transport != NULL); buffer = construct_open_packet (channel); GST_INFO_OBJECT (channel, "Sending channel open for SCTP stream %i " - "label %s protocol %s ordered %s", channel->id, channel->label, - channel->protocol, channel->ordered ? "true" : "false"); + "label %s protocol %s ordered %s", channel->parent.id, + channel->parent.label, channel->parent.protocol, + channel->parent.ordered ? "true" : "false"); - GST_OBJECT_LOCK (channel); - channel->buffered_amount += gst_buffer_get_size (buffer); - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + channel->parent.buffered_amount += gst_buffer_get_size (buffer); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); if (gst_app_src_push_buffer (GST_APP_SRC (channel->appsrc), buffer) == GST_FLOW_OK) { @@ -756,15 +694,15 @@ gst_webrtc_data_channel_start_negotiation (GstWebRTCDataChannel * channel) } static void -_get_sctp_reliability (GstWebRTCDataChannel * channel, +_get_sctp_reliability (WebRTCDataChannel * channel, GstSctpSendMetaPartiallyReliability * reliability, guint * rel_param) { - if (channel->max_retransmits != -1) { + if (channel->parent.max_retransmits != -1) { *reliability = GST_SCTP_SEND_META_PARTIAL_RELIABILITY_RTX; - *rel_param = channel->max_retransmits; - } else if (channel->max_packet_lifetime != -1) { + *rel_param = channel->parent.max_retransmits; + } else if (channel->parent.max_packet_lifetime != -1) { *reliability = GST_SCTP_SEND_META_PARTIAL_RELIABILITY_TTL; - *rel_param = channel->max_packet_lifetime; + *rel_param = channel->parent.max_packet_lifetime; } else { *reliability = GST_SCTP_SEND_META_PARTIAL_RELIABILITY_NONE; *rel_param = 0; @@ -772,15 +710,16 @@ _get_sctp_reliability (GstWebRTCDataChannel * channel, } static gboolean -_is_within_max_message_size (GstWebRTCDataChannel * channel, gsize size) +_is_within_max_message_size (WebRTCDataChannel * channel, gsize size) { return size <= channel->sctp_transport->max_message_size; } static void -gst_webrtc_data_channel_send_data (GstWebRTCDataChannel * channel, +webrtc_data_channel_send_data (GstWebRTCDataChannel * base_channel, GBytes * bytes) { + WebRTCDataChannel *channel = WEBRTC_DATA_CHANNEL (base_channel); GstSctpSendMetaPartiallyReliability reliability; guint rel_param; guint32 ppid; @@ -813,15 +752,15 @@ gst_webrtc_data_channel_send_data (GstWebRTCDataChannel * channel, } _get_sctp_reliability (channel, &reliability, &rel_param); - gst_sctp_buffer_add_send_meta (buffer, ppid, channel->ordered, reliability, - rel_param); + gst_sctp_buffer_add_send_meta (buffer, ppid, channel->parent.ordered, + reliability, rel_param); GST_LOG_OBJECT (channel, "Sending data using buffer %" GST_PTR_FORMAT, buffer); - GST_OBJECT_LOCK (channel); - channel->buffered_amount += gst_buffer_get_size (buffer); - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + channel->parent.buffered_amount += gst_buffer_get_size (buffer); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); ret = gst_app_src_push_buffer (GST_APP_SRC (channel->appsrc), buffer); @@ -835,16 +774,17 @@ gst_webrtc_data_channel_send_data (GstWebRTCDataChannel * channel, } static void -gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, - gchar * str) +webrtc_data_channel_send_string (GstWebRTCDataChannel * base_channel, + const gchar * str) { + WebRTCDataChannel *channel = WEBRTC_DATA_CHANNEL (base_channel); GstSctpSendMetaPartiallyReliability reliability; guint rel_param; guint32 ppid; GstBuffer *buffer; GstFlowReturn ret; - if (!channel->negotiated) + if (!channel->parent.negotiated) g_return_if_fail (channel->opened); g_return_if_fail (channel->sctp_transport != NULL); @@ -873,15 +813,15 @@ gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, } _get_sctp_reliability (channel, &reliability, &rel_param); - gst_sctp_buffer_add_send_meta (buffer, ppid, channel->ordered, reliability, - rel_param); + gst_sctp_buffer_add_send_meta (buffer, ppid, channel->parent.ordered, + reliability, rel_param); GST_TRACE_OBJECT (channel, "Sending string using buffer %" GST_PTR_FORMAT, buffer); - GST_OBJECT_LOCK (channel); - channel->buffered_amount += gst_buffer_get_size (buffer); - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + channel->parent.buffered_amount += gst_buffer_get_size (buffer); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); ret = gst_app_src_push_buffer (GST_APP_SRC (channel->appsrc), buffer); @@ -896,152 +836,37 @@ gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, static void _on_sctp_notify_state_unlocked (GObject * sctp_transport, - GstWebRTCDataChannel * channel) + WebRTCDataChannel * channel) { GstWebRTCSCTPTransportState state; g_object_get (sctp_transport, "state", &state, NULL); if (state == GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED) { - if (channel->negotiated) + if (channel->parent.negotiated) _channel_enqueue_task (channel, (ChannelTask) _emit_on_open, NULL, NULL); } } static void _on_sctp_notify_state (GObject * sctp_transport, GParamSpec * pspec, - GstWebRTCDataChannel * channel) + WebRTCDataChannel * channel) { - GST_OBJECT_LOCK (channel); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); _on_sctp_notify_state_unlocked (sctp_transport, channel); - GST_OBJECT_UNLOCK (channel); -} - -void -gst_webrtc_data_channel_set_sctp_transport (GstWebRTCDataChannel * channel, - GstWebRTCSCTPTransport * sctp) -{ - g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); - g_return_if_fail (GST_IS_WEBRTC_SCTP_TRANSPORT (sctp)); - - GST_OBJECT_LOCK (channel); - if (channel->sctp_transport) - g_signal_handlers_disconnect_by_data (channel->sctp_transport, channel); - - gst_object_replace ((GstObject **) & channel->sctp_transport, - GST_OBJECT (sctp)); - - if (sctp) { - g_signal_connect (sctp, "stream-reset", G_CALLBACK (_on_sctp_reset_stream), - channel); - g_signal_connect (sctp, "notify::state", G_CALLBACK (_on_sctp_notify_state), - channel); - _on_sctp_notify_state_unlocked (G_OBJECT (sctp), channel); - } - GST_OBJECT_UNLOCK (channel); -} - -static void -gst_webrtc_data_channel_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); - - GST_OBJECT_LOCK (channel); - switch (prop_id) { - case PROP_LABEL: - channel->label = g_value_dup_string (value); - break; - case PROP_ORDERED: - channel->ordered = g_value_get_boolean (value); - break; - case PROP_MAX_PACKET_LIFETIME: - channel->max_packet_lifetime = g_value_get_int (value); - break; - case PROP_MAX_RETRANSMITS: - channel->max_retransmits = g_value_get_int (value); - break; - case PROP_PROTOCOL: - channel->protocol = g_value_dup_string (value); - break; - case PROP_NEGOTIATED: - channel->negotiated = g_value_get_boolean (value); - break; - case PROP_ID: - channel->id = g_value_get_int (value); - break; - case PROP_PRIORITY: - channel->priority = g_value_get_enum (value); - break; - case PROP_BUFFERED_AMOUNT_LOW_THRESHOLD: - channel->buffered_amount_low_threshold = g_value_get_uint64 (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); } static void -gst_webrtc_data_channel_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) +_emit_low_threshold (WebRTCDataChannel * channel, gpointer user_data) { - GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); - - GST_OBJECT_LOCK (channel); - switch (prop_id) { - case PROP_LABEL: - g_value_set_string (value, channel->label); - break; - case PROP_ORDERED: - g_value_set_boolean (value, channel->ordered); - break; - case PROP_MAX_PACKET_LIFETIME: - g_value_set_int (value, channel->max_packet_lifetime); - break; - case PROP_MAX_RETRANSMITS: - g_value_set_int (value, channel->max_retransmits); - break; - case PROP_PROTOCOL: - g_value_set_string (value, channel->protocol); - break; - case PROP_NEGOTIATED: - g_value_set_boolean (value, channel->negotiated); - break; - case PROP_ID: - g_value_set_int (value, channel->id); - break; - case PROP_PRIORITY: - g_value_set_enum (value, channel->priority); - break; - case PROP_READY_STATE: - g_value_set_enum (value, channel->ready_state); - break; - case PROP_BUFFERED_AMOUNT: - g_value_set_uint64 (value, channel->buffered_amount); - break; - case PROP_BUFFERED_AMOUNT_LOW_THRESHOLD: - g_value_set_uint64 (value, channel->buffered_amount_low_threshold); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (channel); -} - -static void -_emit_low_threshold (GstWebRTCDataChannel * channel, gpointer user_data) -{ - GST_LOG_OBJECT (channel, "Low threshold reached"); - g_signal_emit (channel, - gst_webrtc_data_channel_signals[SIGNAL_ON_BUFFERED_AMOUNT_LOW], 0); + gst_webrtc_data_channel_on_buffered_amount_low (GST_WEBRTC_DATA_CHANNEL + (channel)); } static GstPadProbeReturn on_appsrc_data (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { - GstWebRTCDataChannel *channel = user_data; + WebRTCDataChannel *channel = user_data; guint64 prev_amount; guint64 size = 0; @@ -1054,21 +879,27 @@ on_appsrc_data (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) } if (size > 0) { - GST_OBJECT_LOCK (channel); - prev_amount = channel->buffered_amount; - channel->buffered_amount -= size; - if (prev_amount > channel->buffered_amount_low_threshold && - channel->buffered_amount < channel->buffered_amount_low_threshold) { - _channel_enqueue_task (channel, (ChannelTask) _emit_low_threshold, - NULL, NULL); + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + prev_amount = channel->parent.buffered_amount; + channel->parent.buffered_amount -= size; + GST_TRACE_OBJECT (channel, "checking low-threshold: prev %" + G_GUINT64_FORMAT " low-threshold %" G_GUINT64_FORMAT " buffered %" + G_GUINT64_FORMAT, prev_amount, + channel->parent.buffered_amount_low_threshold, + channel->parent.buffered_amount); + if (prev_amount >= channel->parent.buffered_amount_low_threshold + && channel->parent.buffered_amount < + channel->parent.buffered_amount_low_threshold) { + _channel_enqueue_task (channel, (ChannelTask) _emit_low_threshold, NULL, + NULL); } - if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING - && channel->buffered_amount <= 0) { + if (channel->parent.ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING + && channel->parent.buffered_amount <= 0) { _channel_enqueue_task (channel, (ChannelTask) _close_sctp_stream, NULL, NULL); } - GST_OBJECT_UNLOCK (channel); + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); } return GST_PAD_PROBE_OK; @@ -1077,7 +908,7 @@ on_appsrc_data (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) static void gst_webrtc_data_channel_constructed (GObject * object) { - GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); + WebRTCDataChannel *channel = WEBRTC_DATA_CHANNEL (object); GstPad *pad; GstCaps *caps; @@ -1104,7 +935,7 @@ gst_webrtc_data_channel_constructed (GObject * object) static void gst_webrtc_data_channel_finalize (GObject * object) { - GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); + WebRTCDataChannel *channel = WEBRTC_DATA_CHANNEL (object); if (channel->src_probe) { GstPad *pad = gst_element_get_static_pad (channel->appsrc, "src"); @@ -1113,12 +944,6 @@ gst_webrtc_data_channel_finalize (GObject * object) channel->src_probe = 0; } - g_free (channel->label); - channel->label = NULL; - - g_free (channel->protocol); - channel->protocol = NULL; - if (channel->sctp_transport) g_signal_handlers_disconnect_by_data (channel->sctp_transport, channel); g_clear_object (&channel->sctp_transport); @@ -1130,194 +955,67 @@ gst_webrtc_data_channel_finalize (GObject * object) } static void -gst_webrtc_data_channel_class_init (GstWebRTCDataChannelClass * klass) +webrtc_data_channel_class_init (WebRTCDataChannelClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; + GstWebRTCDataChannelClass *channel_class = + (GstWebRTCDataChannelClass *) klass; gobject_class->constructed = gst_webrtc_data_channel_constructed; - gobject_class->get_property = gst_webrtc_data_channel_get_property; - gobject_class->set_property = gst_webrtc_data_channel_set_property; gobject_class->finalize = gst_webrtc_data_channel_finalize; - g_object_class_install_property (gobject_class, - PROP_LABEL, - g_param_spec_string ("label", - "Label", "Data channel label", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_ORDERED, - g_param_spec_boolean ("ordered", - "Ordered", "Using ordered transmission mode", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_MAX_PACKET_LIFETIME, - g_param_spec_int ("max-packet-lifetime", - "Maximum Packet Lifetime", - "Maximum number of milliseconds that transmissions and " - "retransmissions may occur in unreliable mode (-1 = unset)", - -1, G_MAXUINT16, -1, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_MAX_RETRANSMITS, - g_param_spec_int ("max-retransmits", - "Maximum Retransmits", - "Maximum number of retransmissions attempted in unreliable mode", - -1, G_MAXUINT16, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_PROTOCOL, - g_param_spec_string ("protocol", - "Protocol", "Data channel protocol", - "", - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_NEGOTIATED, - g_param_spec_boolean ("negotiated", - "Negotiated", - "Whether this data channel was negotiated by the application", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_ID, - g_param_spec_int ("id", - "ID", - "ID negotiated by this data channel (-1 = unset)", - -1, G_MAXUINT16, -1, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_PRIORITY, - g_param_spec_enum ("priority", - "Priority", - "The priority of data sent using this data channel", - GST_TYPE_WEBRTC_PRIORITY_TYPE, - GST_WEBRTC_PRIORITY_TYPE_LOW, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_READY_STATE, - g_param_spec_enum ("ready-state", - "Ready State", - "The Ready state of this data channel", - GST_TYPE_WEBRTC_DATA_CHANNEL_STATE, - GST_WEBRTC_DATA_CHANNEL_STATE_NEW, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_BUFFERED_AMOUNT, - g_param_spec_uint64 ("buffered-amount", - "Buffered Amount", - "The amount of data in bytes currently buffered", - 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, - PROP_BUFFERED_AMOUNT_LOW_THRESHOLD, - g_param_spec_uint64 ("buffered-amount-low-threshold", - "Buffered Amount Low Threshold", - "The threshold at which the buffered amount is considered low and " - "the buffered-amount-low signal is emitted", - 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstWebRTCDataChannel::on-open: - * @object: the #GstWebRTCDataChannel - */ - gst_webrtc_data_channel_signals[SIGNAL_ON_OPEN] = - g_signal_new ("on-open", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 0); - - /** - * GstWebRTCDataChannel::on-close: - * @object: the #GstWebRTCDataChannel - */ - gst_webrtc_data_channel_signals[SIGNAL_ON_CLOSE] = - g_signal_new ("on-close", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 0); - - /** - * GstWebRTCDataChannel::on-error: - * @object: the #GstWebRTCDataChannel - * @error: the #GError thrown - */ - gst_webrtc_data_channel_signals[SIGNAL_ON_ERROR] = - g_signal_new ("on-error", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, G_TYPE_ERROR); - - /** - * GstWebRTCDataChannel::on-message-data: - * @object: the #GstWebRTCDataChannel - * @data: (nullable): a #GBytes of the data received - */ - gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_DATA] = - g_signal_new ("on-message-data", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, G_TYPE_BYTES); - - /** - * GstWebRTCDataChannel::on-message-string: - * @object: the #GstWebRTCDataChannel - * @data: (nullable): the data received as a string - */ - gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_STRING] = - g_signal_new ("on-message-string", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 1, G_TYPE_STRING); - - /** - * GstWebRTCDataChannel::on-buffered-amount-low: - * @object: the #GstWebRTCDataChannel - */ - gst_webrtc_data_channel_signals[SIGNAL_ON_BUFFERED_AMOUNT_LOW] = - g_signal_new ("on-buffered-amount-low", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, - G_TYPE_NONE, 0); - - /** - * GstWebRTCDataChannel::send-data: - * @object: the #GstWebRTCDataChannel - * @data: (nullable): a #GBytes with the data - */ - gst_webrtc_data_channel_signals[SIGNAL_SEND_DATA] = - g_signal_new_class_handler ("send-data", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_data_channel_send_data), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_BYTES); - - /** - * GstWebRTCDataChannel::send-string: - * @object: the #GstWebRTCDataChannel - * @data: (nullable): a #GBytes with the data - */ - gst_webrtc_data_channel_signals[SIGNAL_SEND_STRING] = - g_signal_new_class_handler ("send-string", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_data_channel_send_string), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING); - - /** - * GstWebRTCDataChannel::close: - * @object: the #GstWebRTCDataChannel - * - * Close the data channel - */ - gst_webrtc_data_channel_signals[SIGNAL_CLOSE] = - g_signal_new_class_handler ("close", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, - G_CALLBACK (gst_webrtc_data_channel_close), NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 0); + channel_class->send_data = webrtc_data_channel_send_data; + channel_class->send_string = webrtc_data_channel_send_string; + channel_class->close = webrtc_data_channel_close; +} + +static void +webrtc_data_channel_init (WebRTCDataChannel * channel) +{ } static void -gst_webrtc_data_channel_init (GstWebRTCDataChannel * channel) +_data_channel_set_sctp_transport (WebRTCDataChannel * channel, + GstWebRTCSCTPTransport * sctp) { + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + g_return_if_fail (GST_IS_WEBRTC_SCTP_TRANSPORT (sctp)); + + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + if (channel->sctp_transport) + g_signal_handlers_disconnect_by_data (channel->sctp_transport, channel); + + gst_object_replace ((GstObject **) & channel->sctp_transport, + GST_OBJECT (sctp)); + + if (sctp) { + g_signal_connect (sctp, "stream-reset", G_CALLBACK (_on_sctp_reset_stream), + channel); + g_signal_connect (sctp, "notify::state", G_CALLBACK (_on_sctp_notify_state), + channel); + _on_sctp_notify_state_unlocked (G_OBJECT (sctp), channel); + } + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); +} + +void +webrtc_data_channel_link_to_sctp (WebRTCDataChannel * channel, + GstWebRTCSCTPTransport * sctp_transport) +{ + if (sctp_transport && !channel->sctp_transport) { + gint id; + + g_object_get (channel, "id", &id, NULL); + + if (sctp_transport->association_established && id != -1) { + gchar *pad_name; + + _data_channel_set_sctp_transport (channel, sctp_transport); + pad_name = g_strdup_printf ("sink_%u", id); + if (!gst_element_link_pads (channel->appsrc, "src", + channel->sctp_transport->sctpenc, pad_name)) + g_warn_if_reached (); + g_free (pad_name); + } + } } diff --git a/ext/webrtc/webrtcdatachannel.h b/ext/webrtc/webrtcdatachannel.h index 769844171..7ca3c0d17 100644 --- a/ext/webrtc/webrtcdatachannel.h +++ b/ext/webrtc/webrtcdatachannel.h @@ -17,48 +17,36 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __GST_WEBRTC_DATA_CHANNEL_H__ -#define __GST_WEBRTC_DATA_CHANNEL_H__ +#ifndef __WEBRTC_DATA_CHANNEL_H__ +#define __WEBRTC_DATA_CHANNEL_H__ #include #include #include +#include #include "sctptransport.h" G_BEGIN_DECLS -GST_WEBRTC_API -GType gst_webrtc_data_channel_get_type(void); -#define GST_TYPE_WEBRTC_DATA_CHANNEL (gst_webrtc_data_channel_get_type()) -#define GST_WEBRTC_DATA_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_DATA_CHANNEL,GstWebRTCDataChannel)) -#define GST_IS_WEBRTC_DATA_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_DATA_CHANNEL)) -#define GST_WEBRTC_DATA_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_DATA_CHANNEL,GstWebRTCDataChannelClass)) -#define GST_IS_WEBRTC_DATA_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_DATA_CHANNEL)) -#define GST_WEBRTC_DATA_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_DATA_CHANNEL,GstWebRTCDataChannelClass)) +GType webrtc_data_channel_get_type(void); +#define WEBRTC_TYPE_DATA_CHANNEL (webrtc_data_channel_get_type()) +#define WEBRTC_DATA_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),WEBRTC_TYPE_DATA_CHANNEL,WebRTCDataChannel)) +#define WEBRTC_IS_DATA_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),WEBRTC_TYPE_DATA_CHANNEL)) +#define WEBRTC_DATA_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,WEBRTC_TYPE_DATA_CHANNEL,WebRTCDataChannelClass)) +#define WEBRTC_IS_DATA_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,WEBRTC_TYPE_DATA_CHANNEL)) +#define WEBRTC_DATA_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,WEBRTC_TYPE_DATA_CHANNEL,WebRTCDataChannelClass)) -typedef struct _GstWebRTCDataChannel GstWebRTCDataChannel; -typedef struct _GstWebRTCDataChannelClass GstWebRTCDataChannelClass; +typedef struct _WebRTCDataChannel WebRTCDataChannel; +typedef struct _WebRTCDataChannelClass WebRTCDataChannelClass; -struct _GstWebRTCDataChannel +struct _WebRTCDataChannel { - GstObject parent; + GstWebRTCDataChannel parent; GstWebRTCSCTPTransport *sctp_transport; GstElement *appsrc; GstElement *appsink; - gchar *label; - gboolean ordered; - guint max_packet_lifetime; - guint max_retransmits; - gchar *protocol; - gboolean negotiated; - gint id; - GstWebRTCPriorityType priority; - GstWebRTCDataChannelState ready_state; - guint64 buffered_amount; - guint64 buffered_amount_low_threshold; - GstWebRTCBin *webrtcbin; gboolean opened; gulong src_probe; @@ -67,17 +55,18 @@ struct _GstWebRTCDataChannel gpointer _padding[GST_PADDING]; }; -struct _GstWebRTCDataChannelClass +struct _WebRTCDataChannelClass { - GstObjectClass parent_class; + GstWebRTCDataChannelClass parent_class; gpointer _padding[GST_PADDING]; }; -void gst_webrtc_data_channel_start_negotiation (GstWebRTCDataChannel *channel); -void gst_webrtc_data_channel_set_sctp_transport (GstWebRTCDataChannel *channel, - GstWebRTCSCTPTransport *sctp); +void webrtc_data_channel_start_negotiation (WebRTCDataChannel *channel); +G_GNUC_INTERNAL +void webrtc_data_channel_link_to_sctp (WebRTCDataChannel *channel, + GstWebRTCSCTPTransport *sctp_transport); G_END_DECLS -#endif /* __GST_WEBRTC_DATA_CHANNEL_H__ */ +#endif /* __WEBRTC_DATA_CHANNEL_H__ */ diff --git a/ext/webrtc/webrtcsdp.c b/ext/webrtc/webrtcsdp.c index 5a08c8295..8164b7ad8 100644 --- a/ext/webrtc/webrtcsdp.c +++ b/ext/webrtc/webrtcsdp.c @@ -144,9 +144,8 @@ _check_sdp_crypto (SDPSource source, GstWebRTCSessionDescription * sdp, return TRUE; } -#if 0 -static gboolean -_session_has_attribute_key (const GstSDPMessage * msg, const gchar * key) +gboolean +_message_has_attribute_key (const GstSDPMessage * msg, const gchar * key) { int i; for (i = 0; i < gst_sdp_message_attributes_len (msg); i++) { @@ -159,6 +158,7 @@ _session_has_attribute_key (const GstSDPMessage * msg, const gchar * key) return FALSE; } +#if 0 static gboolean _session_has_attribute_key_value (const GstSDPMessage * msg, const gchar * key, const gchar * value) @@ -212,7 +212,7 @@ _media_has_mid (const GstSDPMedia * media, guint media_idx, GError ** error) return TRUE; } -static const gchar * +const gchar * _media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx) { const gchar *ice_ufrag; @@ -227,7 +227,7 @@ _media_get_ice_ufrag (const GstSDPMessage * msg, guint media_idx) return ice_ufrag; } -static const gchar * +const gchar * _media_get_ice_pwd (const GstSDPMessage * msg, guint media_idx) { const gchar *ice_pwd; @@ -319,7 +319,7 @@ validate_sdp (GstWebRTCSignalingState state, SDPSource source, } if (!_media_has_setup (media, i, error)) goto fail; - /* check paramaters in bundle are the same */ + /* check parameters in bundle are the same */ if (media_in_bundle) { const gchar *ice_ufrag = gst_sdp_media_get_attribute_val (media, "ice-ufrag"); @@ -397,6 +397,8 @@ GstWebRTCRTPTransceiverDirection _intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer, GstWebRTCRTPTransceiverDirection answer) { + if (offer == DIR (INACTIVE) || answer == DIR (INACTIVE)) + return DIR (INACTIVE); if (offer == DIR (SENDONLY) && answer == DIR (SENDRECV)) return DIR (RECVONLY); if (offer == DIR (SENDONLY) && answer == DIR (RECVONLY)) @@ -411,6 +413,10 @@ _intersect_answer_directions (GstWebRTCRTPTransceiverDirection offer, return DIR (SENDONLY); if (offer == DIR (SENDRECV) && answer == DIR (RECVONLY)) return DIR (RECVONLY); + if (offer == DIR (RECVONLY) && answer == DIR (RECVONLY)) + return DIR (INACTIVE); + if (offer == DIR (SENDONLY) && answer == DIR (SENDONLY)) + return DIR (INACTIVE); return DIR (NONE); } @@ -431,14 +437,13 @@ _media_replace_direction (GstSDPMedia * media, if (g_strcmp0 (attr->key, "sendonly") == 0 || g_strcmp0 (attr->key, "sendrecv") == 0 - || g_strcmp0 (attr->key, "recvonly") == 0) { + || g_strcmp0 (attr->key, "recvonly") == 0 + || g_strcmp0 (attr->key, "inactive") == 0) { GstSDPAttribute new_attr = { 0, }; GST_TRACE ("replace %s with %s", attr->key, dir_str); gst_sdp_attribute_set (&new_attr, dir_str, ""); gst_sdp_media_replace_attribute (media, i, &new_attr); -#ifdef __TIZEN__ g_free (dir_str); -#endif return; } } @@ -710,21 +715,56 @@ _generate_ice_credentials (gchar ** ufrag, gchar ** password) int _get_sctp_port_from_media (const GstSDPMedia * media) { - int sctpmap = -1, i; + int i; + const gchar *format; + gchar *endptr; - for (i = 0; i < gst_sdp_media_attributes_len (media); i++) { - const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i); + if (gst_sdp_media_formats_len (media) != 1) { + /* only exactly one format is supported */ + return -1; + } - if (g_strcmp0 (attr->key, "sctp-port") == 0) { - return atoi (attr->value); - } else if (g_strcmp0 (attr->key, "sctpmap") == 0) { - sctpmap = atoi (attr->value); + format = gst_sdp_media_get_format (media, 0); + + if (g_strcmp0 (format, "webrtc-datachannel") == 0) { + /* draft-ietf-mmusic-sctp-sdp-21, e.g. Firefox 63 and later */ + + for (i = 0; i < gst_sdp_media_attributes_len (media); i++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i); + + if (g_strcmp0 (attr->key, "sctp-port") == 0) { + gint64 port = g_ascii_strtoll (attr->value, &endptr, 10); + if (endptr == attr->value) { + /* conversion error */ + return -1; + } + return port; + } + } + } else { + /* draft-ietf-mmusic-sctp-sdp-05, e.g. Chrome as recent as 75 */ + gint64 port = g_ascii_strtoll (format, &endptr, 10); + if (endptr == format) { + /* conversion error */ + return -1; + } + + for (i = 0; i < gst_sdp_media_attributes_len (media); i++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i); + + if (g_strcmp0 (attr->key, "sctpmap") == 0 && atoi (attr->value) == port) { + /* a=sctpmap:5000 webrtc-datachannel 256 */ + gchar **parts = g_strsplit (attr->value, " ", 3); + if (!parts[1] || g_strcmp0 (parts[1], "webrtc-datachannel") != 0) { + port = -1; + } + g_strfreev (parts); + return port; + } } } - if (sctpmap >= 0) - GST_LOG ("no sctp-port attribute in media"); - return sctpmap; + return -1; } guint64 @@ -768,6 +808,21 @@ _message_media_is_datachannel (const GstSDPMessage * msg, guint media_id) return TRUE; } +guint +_message_get_datachannel_index (const GstSDPMessage * msg) +{ + guint i; + + for (i = 0; i < gst_sdp_message_medias_len (msg); i++) { + if (_message_media_is_datachannel (msg, i)) { + g_assert (i < G_MAXUINT); + return i; + } + } + + return G_MAXUINT; +} + void _get_ice_credentials_from_sdp_media (const GstSDPMessage * sdp, guint media_idx, gchar ** ufrag, gchar ** pwd) @@ -833,6 +888,8 @@ _parse_bundle (GstSDPMessage * sdp, GStrv * bundled) if (!(*bundled)[0]) { GST_ERROR ("Invalid format for BUNDLE group, expected at least " "one mid (%s)", group); + g_strfreev (*bundled); + *bundled = NULL; goto done; } } else { @@ -865,3 +922,19 @@ _get_bundle_index (GstSDPMessage * sdp, GStrv bundled, guint * idx) return ret; } + +gboolean +_media_is_bundle_only (const GstSDPMedia * media) +{ + int i; + + for (i = 0; i < gst_sdp_media_attributes_len (media); i++) { + const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i); + + if (g_strcmp0 (attr->key, "bundle-only") == 0) { + return TRUE; + } + } + + return FALSE; +} diff --git a/ext/webrtc/webrtcsdp.h b/ext/webrtc/webrtcsdp.h index a9a86fce5..1501cbc93 100644 --- a/ext/webrtc/webrtcsdp.h +++ b/ext/webrtc/webrtcsdp.h @@ -89,6 +89,11 @@ void _get_ice_credentials_from_sdp_media (con G_GNUC_INTERNAL gboolean _message_media_is_datachannel (const GstSDPMessage * msg, guint media_id); +G_GNUC_INTERNAL +guint _message_get_datachannel_index (const GstSDPMessage * msg); +G_GNUC_INTERNAL +gboolean _message_has_attribute_key (const GstSDPMessage * msg, + const gchar * key); G_GNUC_INTERNAL gboolean _get_bundle_index (GstSDPMessage * sdp, @@ -98,4 +103,14 @@ G_GNUC_INTERNAL gboolean _parse_bundle (GstSDPMessage * sdp, GStrv * bundled); +G_GNUC_INTERNAL +const gchar * _media_get_ice_pwd (const GstSDPMessage * msg, + guint media_idx); +G_GNUC_INTERNAL +const gchar * _media_get_ice_ufrag (const GstSDPMessage * msg, + guint media_idx); + +G_GNUC_INTERNAL +gboolean _media_is_bundle_only (const GstSDPMedia * sdp); + #endif /* __WEBRTC_UTILS_H__ */ diff --git a/ext/webrtc/webrtctransceiver.c b/ext/webrtc/webrtctransceiver.c index c1a3faa1c..f26536741 100644 --- a/ext/webrtc/webrtctransceiver.c +++ b/ext/webrtc/webrtctransceiver.c @@ -25,9 +25,14 @@ #include "utils.h" #include "webrtctransceiver.h" +#define GST_CAT_DEFAULT webrtc_transceiver_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + #define webrtc_transceiver_parent_class parent_class -G_DEFINE_TYPE (WebRTCTransceiver, webrtc_transceiver, - GST_TYPE_WEBRTC_RTP_TRANSCEIVER); +G_DEFINE_TYPE_WITH_CODE (WebRTCTransceiver, webrtc_transceiver, + GST_TYPE_WEBRTC_RTP_TRANSCEIVER, + GST_DEBUG_CATEGORY_INIT (webrtc_transceiver_debug, + "webrtctransceiver", 0, "webrtctransceiver");); #define DEFAULT_FEC_TYPE GST_WEBRTC_FEC_TYPE_NONE #define DEFAULT_DO_NACK FALSE @@ -166,6 +171,8 @@ webrtc_transceiver_finalize (GObject * object) gst_structure_free (trans->local_rtx_ssrc_map); trans->local_rtx_ssrc_map = NULL; + gst_caps_replace (&trans->last_configured_caps, NULL); + G_OBJECT_CLASS (parent_class)->finalize (object); } diff --git a/ext/webrtc/webrtctransceiver.h b/ext/webrtc/webrtctransceiver.h index f1e338f66..c03730415 100644 --- a/ext/webrtc/webrtctransceiver.h +++ b/ext/webrtc/webrtctransceiver.h @@ -44,6 +44,8 @@ struct _WebRTCTransceiver GstWebRTCFECType fec_type; guint fec_percentage; gboolean do_nack; + + GstCaps *last_configured_caps; }; struct _WebRTCTransceiverClass diff --git a/gst-libs/gst/webrtc/Makefile.am b/gst-libs/gst/webrtc/Makefile.am index edc9de0aa..c95b230c8 100644 --- a/gst-libs/gst/webrtc/Makefile.am +++ b/gst-libs/gst/webrtc/Makefile.am @@ -18,7 +18,8 @@ libgstwebrtc_@GST_API_VERSION@_la_SOURCES = \ rtcsessiondescription.c \ rtpreceiver.c \ rtpsender.c \ - rtptransceiver.c + rtptransceiver.c \ + datachannel.c nodist_libgstwebrtc_@GST_API_VERSION@_la_SOURCES = $(built_sources) @@ -31,7 +32,8 @@ libgstwebrtc_@GST_API_VERSION@include_HEADERS = \ rtpsender.h \ rtptransceiver.h \ webrtc_fwd.h \ - webrtc.h + webrtc.h \ + datachannel.h nodist_libgstwebrtc_@GST_API_VERSION@include_HEADERS = $(built_headers) diff --git a/gst-libs/gst/webrtc/datachannel.c b/gst-libs/gst/webrtc/datachannel.c new file mode 100644 index 000000000..ee0be6030 --- /dev/null +++ b/gst-libs/gst/webrtc/datachannel.c @@ -0,0 +1,559 @@ +/* GStreamer + * Copyright (C) 2017 Matthew Waters + * Copyright (C) 2020 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:gstwebrtc-datachannel + * @short_description: RTCDataChannel object + * @title: GstWebRTCDataChannel + * + * + * + * Since: 1.18 + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "datachannel.h" + +#define GST_CAT_DEFAULT gst_webrtc_data_channel_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +#define gst_webrtc_data_channel_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCDataChannel, gst_webrtc_data_channel, + G_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_data_channel_debug, + "webrtcdatachannel", 0, "webrtcdatachannel");); + +enum +{ + SIGNAL_0, + SIGNAL_ON_OPEN, + SIGNAL_ON_CLOSE, + SIGNAL_ON_ERROR, + SIGNAL_ON_MESSAGE_DATA, + SIGNAL_ON_MESSAGE_STRING, + SIGNAL_ON_BUFFERED_AMOUNT_LOW, + SIGNAL_SEND_DATA, + SIGNAL_SEND_STRING, + SIGNAL_CLOSE, + LAST_SIGNAL, +}; + +enum +{ + PROP_0, + PROP_LABEL, + PROP_ORDERED, + PROP_MAX_PACKET_LIFETIME, + PROP_MAX_RETRANSMITS, + PROP_PROTOCOL, + PROP_NEGOTIATED, + PROP_ID, + PROP_PRIORITY, + PROP_READY_STATE, + PROP_BUFFERED_AMOUNT, + PROP_BUFFERED_AMOUNT_LOW_THRESHOLD, +}; + +static guint gst_webrtc_data_channel_signals[LAST_SIGNAL] = { 0 }; + +static void +gst_webrtc_data_channel_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); + + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + switch (prop_id) { + case PROP_LABEL: + g_free (channel->label); + channel->label = g_value_dup_string (value); + break; + case PROP_ORDERED: + channel->ordered = g_value_get_boolean (value); + break; + case PROP_MAX_PACKET_LIFETIME: + channel->max_packet_lifetime = g_value_get_int (value); + break; + case PROP_MAX_RETRANSMITS: + channel->max_retransmits = g_value_get_int (value); + break; + case PROP_PROTOCOL: + g_free (channel->protocol); + channel->protocol = g_value_dup_string (value); + break; + case PROP_NEGOTIATED: + channel->negotiated = g_value_get_boolean (value); + break; + case PROP_ID: + channel->id = g_value_get_int (value); + break; + case PROP_PRIORITY: + channel->priority = g_value_get_enum (value); + break; + case PROP_BUFFERED_AMOUNT_LOW_THRESHOLD: + channel->buffered_amount_low_threshold = g_value_get_uint64 (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); +} + +static void +gst_webrtc_data_channel_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); + + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + switch (prop_id) { + case PROP_LABEL: + g_value_set_string (value, channel->label); + break; + case PROP_ORDERED: + g_value_set_boolean (value, channel->ordered); + break; + case PROP_MAX_PACKET_LIFETIME: + g_value_set_int (value, channel->max_packet_lifetime); + break; + case PROP_MAX_RETRANSMITS: + g_value_set_int (value, channel->max_retransmits); + break; + case PROP_PROTOCOL: + g_value_set_string (value, channel->protocol); + break; + case PROP_NEGOTIATED: + g_value_set_boolean (value, channel->negotiated); + break; + case PROP_ID: + g_value_set_int (value, channel->id); + break; + case PROP_PRIORITY: + g_value_set_enum (value, channel->priority); + break; + case PROP_READY_STATE: + g_value_set_enum (value, channel->ready_state); + break; + case PROP_BUFFERED_AMOUNT: + g_value_set_uint64 (value, channel->buffered_amount); + break; + case PROP_BUFFERED_AMOUNT_LOW_THRESHOLD: + g_value_set_uint64 (value, channel->buffered_amount_low_threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); +} + +static void +gst_webrtc_data_channel_finalize (GObject * object) +{ + GstWebRTCDataChannel *channel = GST_WEBRTC_DATA_CHANNEL (object); + + g_free (channel->label); + channel->label = NULL; + + g_free (channel->protocol); + channel->protocol = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_webrtc_data_channel_class_init (GstWebRTCDataChannelClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->get_property = gst_webrtc_data_channel_get_property; + gobject_class->set_property = gst_webrtc_data_channel_set_property; + gobject_class->finalize = gst_webrtc_data_channel_finalize; + + g_object_class_install_property (gobject_class, + PROP_LABEL, + g_param_spec_string ("label", + "Label", "Data channel label", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ORDERED, + g_param_spec_boolean ("ordered", + "Ordered", "Using ordered transmission mode", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_MAX_PACKET_LIFETIME, + g_param_spec_int ("max-packet-lifetime", + "Maximum Packet Lifetime", + "Maximum number of milliseconds that transmissions and " + "retransmissions may occur in unreliable mode (-1 = unset)", + -1, G_MAXUINT16, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_MAX_RETRANSMITS, + g_param_spec_int ("max-retransmits", + "Maximum Retransmits", + "Maximum number of retransmissions attempted in unreliable mode", + -1, G_MAXUINT16, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_PROTOCOL, + g_param_spec_string ("protocol", + "Protocol", "Data channel protocol", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_NEGOTIATED, + g_param_spec_boolean ("negotiated", + "Negotiated", + "Whether this data channel was negotiated by the application", FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_ID, + g_param_spec_int ("id", + "ID", + "ID negotiated by this data channel (-1 = unset)", + -1, G_MAXUINT16, -1, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_PRIORITY, + g_param_spec_enum ("priority", + "Priority", + "The priority of data sent using this data channel", + GST_TYPE_WEBRTC_PRIORITY_TYPE, + GST_WEBRTC_PRIORITY_TYPE_LOW, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_READY_STATE, + g_param_spec_enum ("ready-state", + "Ready State", + "The Ready state of this data channel", + GST_TYPE_WEBRTC_DATA_CHANNEL_STATE, + GST_WEBRTC_DATA_CHANNEL_STATE_NEW, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_BUFFERED_AMOUNT, + g_param_spec_uint64 ("buffered-amount", + "Buffered Amount", + "The amount of data in bytes currently buffered", + 0, G_MAXUINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_BUFFERED_AMOUNT_LOW_THRESHOLD, + g_param_spec_uint64 ("buffered-amount-low-threshold", + "Buffered Amount Low Threshold", + "The threshold at which the buffered amount is considered low and " + "the buffered-amount-low signal is emitted", + 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstWebRTCDataChannel::on-open: + * @object: the #GstWebRTCDataChannel + */ + gst_webrtc_data_channel_signals[SIGNAL_ON_OPEN] = + g_signal_new ("on-open", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + /** + * GstWebRTCDataChannel::on-close: + * @object: the #GstWebRTCDataChannel + */ + gst_webrtc_data_channel_signals[SIGNAL_ON_CLOSE] = + g_signal_new ("on-close", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + /** + * GstWebRTCDataChannel::on-error: + * @object: the #GstWebRTCDataChannel + * @error: the #GError thrown + */ + gst_webrtc_data_channel_signals[SIGNAL_ON_ERROR] = + g_signal_new ("on-error", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR); + + /** + * GstWebRTCDataChannel::on-message-data: + * @object: the #GstWebRTCDataChannel + * @data: (nullable): a #GBytes of the data received + */ + gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_DATA] = + g_signal_new ("on-message-data", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BYTES); + + /** + * GstWebRTCDataChannel::on-message-string: + * @object: the #GstWebRTCDataChannel + * @data: (nullable): the data received as a string + */ + gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_STRING] = + g_signal_new ("on-message-string", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); + + /** + * GstWebRTCDataChannel::on-buffered-amount-low: + * @object: the #GstWebRTCDataChannel + */ + gst_webrtc_data_channel_signals[SIGNAL_ON_BUFFERED_AMOUNT_LOW] = + g_signal_new ("on-buffered-amount-low", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + /** + * GstWebRTCDataChannel::send-data: + * @object: the #GstWebRTCDataChannel + * @data: (nullable): a #GBytes with the data + */ + gst_webrtc_data_channel_signals[SIGNAL_SEND_DATA] = + g_signal_new_class_handler ("send-data", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_webrtc_data_channel_send_data), NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_BYTES); + + /** + * GstWebRTCDataChannel::send-string: + * @object: the #GstWebRTCDataChannel + * @data: (nullable): the data to send as a string + */ + gst_webrtc_data_channel_signals[SIGNAL_SEND_STRING] = + g_signal_new_class_handler ("send-string", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_webrtc_data_channel_send_string), NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + + /** + * GstWebRTCDataChannel::close: + * @object: the #GstWebRTCDataChannel + * + * Close the data channel + */ + gst_webrtc_data_channel_signals[SIGNAL_CLOSE] = + g_signal_new_class_handler ("close", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_CALLBACK (gst_webrtc_data_channel_close), NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +static void +gst_webrtc_data_channel_init (GstWebRTCDataChannel * channel) +{ + g_mutex_init (&channel->lock); +} + +/** + * gst_webrtc_data_channel_on_open: + * @channel: a #GstWebRTCDataChannel + * + * Signal that the data channel was opened. Should only be used by subclasses. + */ +void +gst_webrtc_data_channel_on_open (GstWebRTCDataChannel * channel) +{ + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING || + channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) { + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); + return; + } + + if (channel->ready_state != GST_WEBRTC_DATA_CHANNEL_STATE_OPEN) { + channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_OPEN; + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); + g_object_notify (G_OBJECT (channel), "ready-state"); + + GST_INFO_OBJECT (channel, "We are open and ready for data!"); + } else { + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); + } + + GST_INFO_OBJECT (channel, "Opened"); + + g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_OPEN], 0, + NULL); +} + +/** + * gst_webrtc_data_channel_on_close: + * @channel: a #GstWebRTCDataChannel + * + * Signal that the data channel was closed. Should only be used by subclasses. + */ +void +gst_webrtc_data_channel_on_close (GstWebRTCDataChannel * channel) +{ + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + GST_INFO_OBJECT (channel, "Closed"); + + GST_WEBRTC_DATA_CHANNEL_LOCK (channel); + if (channel->ready_state == GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED) { + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); + return; + } + + channel->ready_state = GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED; + GST_WEBRTC_DATA_CHANNEL_UNLOCK (channel); + + g_object_notify (G_OBJECT (channel), "ready-state"); + GST_INFO_OBJECT (channel, "We are closed for data"); + + g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_CLOSE], 0, + NULL); +} + +/** + * gst_webrtc_data_channel_on_error: + * @channel: a #GstWebRTCDataChannel + * @error: (transfer full): a #GError + * + * Signal that the data channel had an error. Should only be used by subclasses. + */ +void +gst_webrtc_data_channel_on_error (GstWebRTCDataChannel * channel, + GError * error) +{ + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + g_return_if_fail (error != NULL); + + GST_WARNING_OBJECT (channel, "Error: %s", GST_STR_NULL (error->message)); + + g_signal_emit (channel, gst_webrtc_data_channel_signals[SIGNAL_ON_ERROR], 0, + error); +} + +/** + * gst_webrtc_data_channel_on_message_data: + * @channel: a #GstWebRTCDataChannel + * @data: (nullable): a #GBytes or %NULL + * + * Signal that the data channel received a data message. Should only be used by subclasses. + */ +void +gst_webrtc_data_channel_on_message_data (GstWebRTCDataChannel * channel, + GBytes * data) +{ + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + GST_LOG_OBJECT (channel, "Have data %p", data); + g_signal_emit (channel, + gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_DATA], 0, data); +} + +/** + * gst_webrtc_data_channel_on_message_string: + * @channel: a #GstWebRTCDataChannel + * @str: (nullable): a string or %NULL + * + * Signal that the data channel received a string message. Should only be used by subclasses. + */ +void +gst_webrtc_data_channel_on_message_string (GstWebRTCDataChannel * channel, + const gchar * str) +{ + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + GST_LOG_OBJECT (channel, "Have string %p", str); + g_signal_emit (channel, + gst_webrtc_data_channel_signals[SIGNAL_ON_MESSAGE_STRING], 0, str); +} + +/** + * gst_webrtc_data_channel_on_buffered_amount_low: + * @channel: a #GstWebRTCDataChannel + * + * Signal that the data channel reached a low buffered amount. Should only be used by subclasses. + */ +void +gst_webrtc_data_channel_on_buffered_amount_low (GstWebRTCDataChannel * channel) +{ + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + GST_LOG_OBJECT (channel, "Low threshold reached"); + g_signal_emit (channel, + gst_webrtc_data_channel_signals[SIGNAL_ON_BUFFERED_AMOUNT_LOW], 0); +} + +/** + * gst_webrtc_data_channel_send_data: + * @channel: a #GstWebRTCDataChannel + * @data: (nullable): a #GBytes or %NULL + * + * Send @data as a data message over @channel. + */ +void +gst_webrtc_data_channel_send_data (GstWebRTCDataChannel * channel, + GBytes * data) +{ + GstWebRTCDataChannelClass *klass; + + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + klass = GST_WEBRTC_DATA_CHANNEL_GET_CLASS (channel); + klass->send_data (channel, data); +} + +/** + * gst_webrtc_data_channel_send_string: + * @channel: a #GstWebRTCDataChannel + * @str: (nullable): a string or %NULL + * + * Send @str as a string message over @channel. + */ +void +gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, + const gchar * str) +{ + GstWebRTCDataChannelClass *klass; + + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + klass = GST_WEBRTC_DATA_CHANNEL_GET_CLASS (channel); + klass->send_string (channel, str); +} + +/** + * gst_webrtc_data_channel_close: + * @channel: a #GstWebRTCDataChannel + * + * Close the @channel. + */ +void +gst_webrtc_data_channel_close (GstWebRTCDataChannel * channel) +{ + GstWebRTCDataChannelClass *klass; + + g_return_if_fail (GST_IS_WEBRTC_DATA_CHANNEL (channel)); + + klass = GST_WEBRTC_DATA_CHANNEL_GET_CLASS (channel); + klass->close (channel); +} diff --git a/gst-libs/gst/webrtc/datachannel.h b/gst-libs/gst/webrtc/datachannel.h new file mode 100644 index 000000000..79b536f5b --- /dev/null +++ b/gst-libs/gst/webrtc/datachannel.h @@ -0,0 +1,115 @@ +/* GStreamer + * Copyright (C) 2018 Matthew Waters + * Copyright (C) 2020 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_WEBRTC_DATA_CHANNEL_H__ +#define __GST_WEBRTC_DATA_CHANNEL_H__ + +#include +#include + +G_BEGIN_DECLS + +GST_WEBRTC_API +GType gst_webrtc_data_channel_get_type(void); + +#define GST_TYPE_WEBRTC_DATA_CHANNEL (gst_webrtc_data_channel_get_type()) +#define GST_WEBRTC_DATA_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WEBRTC_DATA_CHANNEL,GstWebRTCDataChannel)) +#define GST_IS_WEBRTC_DATA_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WEBRTC_DATA_CHANNEL)) +#define GST_WEBRTC_DATA_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_WEBRTC_DATA_CHANNEL,GstWebRTCDataChannelClass)) +#define GST_IS_WEBRTC_DATA_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_DATA_CHANNEL)) +#define GST_WEBRTC_DATA_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_DATA_CHANNEL,GstWebRTCDataChannelClass)) + +#define GST_WEBRTC_DATA_CHANNEL_LOCK(channel) g_mutex_lock(&((GstWebRTCDataChannel *)(channel))->lock) +#define GST_WEBRTC_DATA_CHANNEL_UNLOCK(channel) g_mutex_unlock(&((GstWebRTCDataChannel *)(channel))->lock) + +/** + * GstWebRTCDataChannel: + * + * Since: 1.18 + */ +struct _GstWebRTCDataChannel +{ + GObject parent; + + GMutex lock; + + gchar *label; + gboolean ordered; + guint max_packet_lifetime; + guint max_retransmits; + gchar *protocol; + gboolean negotiated; + gint id; + GstWebRTCPriorityType priority; + GstWebRTCDataChannelState ready_state; + guint64 buffered_amount; + guint64 buffered_amount_low_threshold; + + gpointer _padding[GST_PADDING]; +}; + +/** + * GstWebRTCDataChannelClass: + * + * Since: 1.18 + */ +struct _GstWebRTCDataChannelClass +{ + GObjectClass parent_class; + + void (*send_data) (GstWebRTCDataChannel * channel, GBytes *data); + void (*send_string) (GstWebRTCDataChannel * channel, const gchar *str); + void (*close) (GstWebRTCDataChannel * channel); + + gpointer _padding[GST_PADDING]; +}; + +GST_WEBRTC_API +void gst_webrtc_data_channel_on_open (GstWebRTCDataChannel * channel); + +GST_WEBRTC_API +void gst_webrtc_data_channel_on_close (GstWebRTCDataChannel * channel); + +GST_WEBRTC_API +void gst_webrtc_data_channel_on_error (GstWebRTCDataChannel * channel, GError * error); + +GST_WEBRTC_API +void gst_webrtc_data_channel_on_message_data (GstWebRTCDataChannel * channel, GBytes * data); + +GST_WEBRTC_API +void gst_webrtc_data_channel_on_message_string (GstWebRTCDataChannel * channel, const gchar * str); + +GST_WEBRTC_API +void gst_webrtc_data_channel_on_buffered_amount_low (GstWebRTCDataChannel * channel); + +GST_WEBRTC_API +void gst_webrtc_data_channel_send_data (GstWebRTCDataChannel * channel, GBytes * data); + +GST_WEBRTC_API +void gst_webrtc_data_channel_send_string (GstWebRTCDataChannel * channel, const gchar * str); + +GST_WEBRTC_API +void gst_webrtc_data_channel_close (GstWebRTCDataChannel * channel); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCDataChannel, g_object_unref) + +G_END_DECLS + +#endif /* __GST_WEBRTC_DATA_CHANNEL_H__ */ diff --git a/gst-libs/gst/webrtc/dtlstransport.c b/gst-libs/gst/webrtc/dtlstransport.c index c3b2d519d..2c7135b1d 100644 --- a/gst-libs/gst/webrtc/dtlstransport.c +++ b/gst-libs/gst/webrtc/dtlstransport.c @@ -23,7 +23,7 @@ * @title: GstWebRTCDTLSTransport * @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPReceiver, #GstWebRTCICETransport * - * https://www.w3.org/TR/webrtc/#rtcdtlstransport + * */ #ifdef HAVE_CONFIG_H @@ -145,6 +145,36 @@ gst_webrtc_dtls_transport_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static void +on_connection_state_changed (GObject * obj, GParamSpec * pspec, + gpointer user_data) +{ + GstWebRTCDTLSTransport *webrtc = GST_WEBRTC_DTLS_TRANSPORT (user_data); + gint state; + + g_object_get (obj, "connection-state", &state, NULL); + switch (state) { + case 0: + webrtc->state = GST_WEBRTC_DTLS_TRANSPORT_STATE_NEW; + break; + case 1: + webrtc->state = GST_WEBRTC_DTLS_TRANSPORT_STATE_CLOSED; + break; + default: + case 2: + webrtc->state = GST_WEBRTC_DTLS_TRANSPORT_STATE_FAILED; + break; + case 3: + webrtc->state = GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTING; + break; + case 4: + webrtc->state = GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED; + break; + } + + g_object_notify (G_OBJECT (webrtc), "state"); +} + static void gst_webrtc_dtls_transport_constructed (GObject * object) { @@ -159,12 +189,15 @@ gst_webrtc_dtls_transport_constructed (GObject * object) webrtc->dtlssrtpenc = gst_element_factory_make ("dtlssrtpenc", NULL); g_object_set (webrtc->dtlssrtpenc, "connection-id", connection_id, - "is-client", webrtc->client, NULL); + "is-client", webrtc->client, "rtp-sync", TRUE, NULL); webrtc->dtlssrtpdec = gst_element_factory_make ("dtlssrtpdec", NULL); g_object_set (webrtc->dtlssrtpdec, "connection-id", connection_id, NULL); g_free (connection_id); + g_signal_connect (webrtc->dtlssrtpenc, "notify::connection-state", + G_CALLBACK (on_connection_state_changed), webrtc); + G_OBJECT_CLASS (parent_class)->constructed (object); } @@ -191,7 +224,6 @@ gst_webrtc_dtls_transport_class_init (GstWebRTCDTLSTransportClass * klass) GST_TYPE_WEBRTC_ICE_TRANSPORT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - /* FIXME: implement */ g_object_class_install_property (gobject_class, PROP_STATE, g_param_spec_enum ("state", "DTLS state", diff --git a/gst-libs/gst/webrtc/dtlstransport.h b/gst-libs/gst/webrtc/dtlstransport.h index 2af197567..feb3944bb 100644 --- a/gst-libs/gst/webrtc/dtlstransport.h +++ b/gst-libs/gst/webrtc/dtlstransport.h @@ -35,6 +35,9 @@ GType gst_webrtc_dtls_transport_get_type(void); #define GST_IS_WEBRTC_DTLS_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_DTLS_TRANSPORT)) #define GST_WEBRTC_DTLS_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_DTLS_TRANSPORT,GstWebRTCDTLSTransportClass)) +/** + * GstWebRTCDTLSTransport: + */ struct _GstWebRTCDTLSTransport { GstObject parent; @@ -65,9 +68,7 @@ GST_WEBRTC_API void gst_webrtc_dtls_transport_set_transport (GstWebRTCDTLSTransport * transport, GstWebRTCICETransport * ice); -#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCDTLSTransport, gst_object_unref) -#endif G_END_DECLS diff --git a/gst-libs/gst/webrtc/icetransport.c b/gst-libs/gst/webrtc/icetransport.c index e6f44378f..21e2cbe9b 100644 --- a/gst-libs/gst/webrtc/icetransport.c +++ b/gst-libs/gst/webrtc/icetransport.c @@ -23,7 +23,7 @@ * @title: GstWebRTCICETransport * @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPReceiver, #GstWebRTCDTLSTransport * - * https://www.w3.org/TR/webrtc/#rtcicetransport + * */ #ifdef HAVE_CONFIG_H @@ -189,8 +189,8 @@ gst_webrtc_ice_transport_class_init (GstWebRTCICETransportClass * klass) */ gst_webrtc_ice_transport_signals[ON_SELECTED_CANDIDATE_PAIR_CHANGE_SIGNAL] = g_signal_new ("on-selected-candidate-pair-change", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 0); + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); /** * GstWebRTC::on-new-candidate: @@ -198,8 +198,8 @@ gst_webrtc_ice_transport_class_init (GstWebRTCICETransportClass * klass) */ gst_webrtc_ice_transport_signals[ON_NEW_CANDIDATE_SIGNAL] = g_signal_new ("on-new-candidate", - G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, - g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_STRING); + G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); } static void diff --git a/gst-libs/gst/webrtc/icetransport.h b/gst-libs/gst/webrtc/icetransport.h index d18c44fd9..c1e56d41e 100644 --- a/gst-libs/gst/webrtc/icetransport.h +++ b/gst-libs/gst/webrtc/icetransport.h @@ -34,6 +34,9 @@ GType gst_webrtc_ice_transport_get_type(void); #define GST_IS_WEBRTC_ICE_TRANSPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_ICE_TRANSPORT)) #define GST_WEBRTC_ICE_TRANSPORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_ICE_TRANSPORT,GstWebRTCICETransportClass)) +/** + * GstWebRTCICETransport: + */ struct _GstWebRTCICETransport { GstObject parent; @@ -71,9 +74,7 @@ void gst_webrtc_ice_transport_selected_pair_change (GstWebRTCIC GST_WEBRTC_API void gst_webrtc_ice_transport_new_candidate (GstWebRTCICETransport * ice, guint stream_id, GstWebRTCICEComponent component, gchar * attr); -#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCICETransport, gst_object_unref) -#endif G_END_DECLS diff --git a/gst-libs/gst/webrtc/meson.build b/gst-libs/gst/webrtc/meson.build index a9f11dc59..981083c2e 100644 --- a/gst-libs/gst/webrtc/meson.build +++ b/gst-libs/gst/webrtc/meson.build @@ -5,6 +5,7 @@ webrtc_sources = [ 'rtpreceiver.c', 'rtpsender.c', 'rtptransceiver.c', + 'datachannel.c', ] webrtc_headers = [ @@ -14,6 +15,7 @@ webrtc_headers = [ 'rtpreceiver.h', 'rtpsender.h', 'rtptransceiver.h', + 'datachannel.h', 'webrtc_fwd.h', 'webrtc.h', ] diff --git a/gst-libs/gst/webrtc/rtcsessiondescription.c b/gst-libs/gst/webrtc/rtcsessiondescription.c index af5cd1c0d..abdf5ca92 100644 --- a/gst-libs/gst/webrtc/rtcsessiondescription.c +++ b/gst-libs/gst/webrtc/rtcsessiondescription.c @@ -22,7 +22,7 @@ * @short_description: RTCSessionDescription object * @title: GstWebRTCSessionDescription * - * https://www.w3.org/TR/webrtc/#rtcsessiondescription-class + * */ #ifdef HAVE_CONFIG_H diff --git a/gst-libs/gst/webrtc/rtcsessiondescription.h b/gst-libs/gst/webrtc/rtcsessiondescription.h index ae6863b9e..5308c549a 100644 --- a/gst-libs/gst/webrtc/rtcsessiondescription.h +++ b/gst-libs/gst/webrtc/rtcsessiondescription.h @@ -38,7 +38,7 @@ GType gst_webrtc_session_description_get_type (void); * @type: the #GstWebRTCSDPType of the description * @sdp: the #GstSDPMessage of the description * - * See https://www.w3.org/TR/webrtc/#rtcsessiondescription-class + * See */ struct _GstWebRTCSessionDescription { @@ -54,9 +54,7 @@ GST_WEBRTC_API void gst_webrtc_session_description_free (GstWebRTCSessionDescription * desc); -#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCSessionDescription, gst_webrtc_session_description_free) -#endif G_END_DECLS diff --git a/gst-libs/gst/webrtc/rtpreceiver.c b/gst-libs/gst/webrtc/rtpreceiver.c index f21d77ef1..768e9876d 100644 --- a/gst-libs/gst/webrtc/rtpreceiver.c +++ b/gst-libs/gst/webrtc/rtpreceiver.c @@ -23,7 +23,7 @@ * @title: GstWebRTCRTPReceiver * @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPTransceiver * - * https://www.w3.org/TR/webrtc/#rtcrtpreceiver-interface + * */ #ifdef HAVE_CONFIG_H diff --git a/gst-libs/gst/webrtc/rtpreceiver.h b/gst-libs/gst/webrtc/rtpreceiver.h index 9502c5cc3..55a9a86fd 100644 --- a/gst-libs/gst/webrtc/rtpreceiver.h +++ b/gst-libs/gst/webrtc/rtpreceiver.h @@ -35,6 +35,9 @@ GType gst_webrtc_rtp_receiver_get_type(void); #define GST_IS_WEBRTC_RTP_RECEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_RTP_RECEIVER)) #define GST_WEBRTC_RTP_RECEIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_RTP_RECEIVER,GstWebRTCRTPReceiverClass)) +/** + * GstWebRTCRTPReceiver: + */ struct _GstWebRTCRTPReceiver { GstObject parent; @@ -62,9 +65,7 @@ GST_WEBRTC_API void gst_webrtc_rtp_receiver_set_rtcp_transport (GstWebRTCRTPReceiver * receiver, GstWebRTCDTLSTransport * transport); -#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCRTPReceiver, gst_object_unref) -#endif G_END_DECLS diff --git a/gst-libs/gst/webrtc/rtpsender.c b/gst-libs/gst/webrtc/rtpsender.c index da743f32d..3a8a9044f 100644 --- a/gst-libs/gst/webrtc/rtpsender.c +++ b/gst-libs/gst/webrtc/rtpsender.c @@ -23,7 +23,7 @@ * @title: GstWebRTCRTPSender * @see_also: #GstWebRTCRTPReceiver, #GstWebRTCRTPTransceiver * - * https://www.w3.org/TR/webrtc/#rtcrtpsender-interface + * */ #ifdef HAVE_CONFIG_H diff --git a/gst-libs/gst/webrtc/rtpsender.h b/gst-libs/gst/webrtc/rtpsender.h index a23551eca..bcaf93c60 100644 --- a/gst-libs/gst/webrtc/rtpsender.h +++ b/gst-libs/gst/webrtc/rtpsender.h @@ -35,6 +35,9 @@ GType gst_webrtc_rtp_sender_get_type(void); #define GST_IS_WEBRTC_RTP_SENDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_RTP_SENDER)) #define GST_WEBRTC_RTP_SENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_RTP_SENDER,GstWebRTCRTPSenderClass)) +/** + * GstWebRTCRTPSender: + */ struct _GstWebRTCRTPSender { GstObject parent; @@ -66,9 +69,7 @@ void gst_webrtc_rtp_sender_set_rtcp_transport (GstWebR GstWebRTCDTLSTransport * transport); -#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCRTPSender, gst_object_unref) -#endif G_END_DECLS diff --git a/gst-libs/gst/webrtc/rtptransceiver.c b/gst-libs/gst/webrtc/rtptransceiver.c index d0d9628d0..08019462a 100644 --- a/gst-libs/gst/webrtc/rtptransceiver.c +++ b/gst-libs/gst/webrtc/rtptransceiver.c @@ -23,7 +23,7 @@ * @title: GstWebRTCRTPTransceiver * @see_also: #GstWebRTCRTPSender, #GstWebRTCRTPReceiver * - * https://www.w3.org/TR/webrtc/#rtcrtptransceiver-interface + * */ #ifdef HAVE_CONFIG_H @@ -39,7 +39,7 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstWebRTCRTPTransceiver, gst_webrtc_rtp_transceiver, GST_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_webrtc_rtp_transceiver_debug, - "webrtctransceiver", 0, "webrtctransceiver"); + "webrtcrtptransceiver", 0, "webrtcrtptransceiver"); ); enum @@ -54,9 +54,9 @@ enum PROP_MID, PROP_SENDER, PROP_RECEIVER, - PROP_STOPPED, // FIXME - PROP_DIRECTION, // FIXME + PROP_DIRECTION, PROP_MLINE, + PROP_STOPPED, // FIXME }; //static guint gst_webrtc_rtp_transceiver_signals[LAST_SIGNAL] = { 0 }; @@ -77,6 +77,9 @@ gst_webrtc_rtp_transceiver_set_property (GObject * object, guint prop_id, case PROP_MLINE: webrtc->mline = g_value_get_uint (value); break; + case PROP_DIRECTION: + webrtc->direction = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -99,6 +102,9 @@ gst_webrtc_rtp_transceiver_get_property (GObject * object, guint prop_id, case PROP_MLINE: g_value_set_uint (value, webrtc->mline); break; + case PROP_DIRECTION: + g_value_set_enum (value, webrtc->direction); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -178,9 +184,25 @@ gst_webrtc_rtp_transceiver_class_init (GstWebRTCRTPTransceiverClass * klass) "Index in the SDP of the Media", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + /** + * GstWebRTCRTPTransceiver:direction: + * + * Direction of the transceiver. + * + * Since: 1.18 + **/ + g_object_class_install_property (gobject_class, + PROP_DIRECTION, + g_param_spec_enum ("direction", "Direction", + "Transceiver direction", + GST_TYPE_WEBRTC_RTP_TRANSCEIVER_DIRECTION, + GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void gst_webrtc_rtp_transceiver_init (GstWebRTCRTPTransceiver * webrtc) { + webrtc->direction = GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE; } diff --git a/gst-libs/gst/webrtc/rtptransceiver.h b/gst-libs/gst/webrtc/rtptransceiver.h index 3c7c92a95..4b2e6e30c 100644 --- a/gst-libs/gst/webrtc/rtptransceiver.h +++ b/gst-libs/gst/webrtc/rtptransceiver.h @@ -36,6 +36,9 @@ GType gst_webrtc_rtp_transceiver_get_type(void); #define GST_IS_WEBRTC_RTP_TRANSCEIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_WEBRTC_RTP_TRANSCEIVER)) #define GST_WEBRTC_RTP_TRANSCEIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_WEBRTC_RTP_TRANSCEIVER,GstWebRTCRTPTransceiverClass)) +/** + * GstWebRTCRTPTransceiver: + */ struct _GstWebRTCRTPTransceiver { GstObject parent; @@ -58,12 +61,11 @@ struct _GstWebRTCRTPTransceiverClass { GstObjectClass parent_class; + /* FIXME; reset */ gpointer _padding[GST_PADDING]; }; -#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstWebRTCRTPTransceiver, gst_object_unref) -#endif G_END_DECLS diff --git a/gst-libs/gst/webrtc/webrtc.h b/gst-libs/gst/webrtc/webrtc.h index 354c15c19..e68a9dba8 100644 --- a/gst-libs/gst/webrtc/webrtc.h +++ b/gst-libs/gst/webrtc/webrtc.h @@ -29,5 +29,6 @@ #include #include #include +#include #endif /* __GST_WEBRTC_WEBRTC_H__ */ diff --git a/gst-libs/gst/webrtc/webrtc_fwd.h b/gst-libs/gst/webrtc/webrtc_fwd.h index 0d00e2b2b..5c727d234 100644 --- a/gst-libs/gst/webrtc/webrtc_fwd.h +++ b/gst-libs/gst/webrtc/webrtc_fwd.h @@ -27,6 +27,11 @@ #include +/** + * SECTION:webrtc_fwd.h + * @title: GstWebRTC Enumerations + */ + #ifndef GST_WEBRTC_API # ifdef BUILDING_GST_WEBRTC # define GST_WEBRTC_API GST_API_EXPORT /* from config.h */ @@ -54,13 +59,16 @@ typedef struct _GstWebRTCSessionDescription GstWebRTCSessionDescription; typedef struct _GstWebRTCRTPTransceiver GstWebRTCRTPTransceiver; typedef struct _GstWebRTCRTPTransceiverClass GstWebRTCRTPTransceiverClass; +typedef struct _GstWebRTCDataChannel GstWebRTCDataChannel; +typedef struct _GstWebRTCDataChannelClass GstWebRTCDataChannelClass; + /** * GstWebRTCDTLSTransportState: - * GST_WEBRTC_DTLS_TRANSPORT_STATE_NEW: new - * GST_WEBRTC_DTLS_TRANSPORT_STATE_CLOSED: closed - * GST_WEBRTC_DTLS_TRANSPORT_STATE_FAILED: failed - * GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTING: connecting - * GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED: connected + * @GST_WEBRTC_DTLS_TRANSPORT_STATE_NEW: new + * @GST_WEBRTC_DTLS_TRANSPORT_STATE_CLOSED: closed + * @GST_WEBRTC_DTLS_TRANSPORT_STATE_FAILED: failed + * @GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTING: connecting + * @GST_WEBRTC_DTLS_TRANSPORT_STATE_CONNECTED: connected */ typedef enum /*< underscore_name=gst_webrtc_dtls_transport_state >*/ { @@ -73,11 +81,11 @@ typedef enum /*< underscore_name=gst_webrtc_dtls_transport_state >*/ /** * GstWebRTCICEGatheringState: - * GST_WEBRTC_ICE_GATHERING_STATE_NEW: new - * GST_WEBRTC_ICE_GATHERING_STATE_GATHERING: gathering - * GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE: complete + * @GST_WEBRTC_ICE_GATHERING_STATE_NEW: new + * @GST_WEBRTC_ICE_GATHERING_STATE_GATHERING: gathering + * @GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE: complete * - * See http://w3c.github.io/webrtc-pc/#dom-rtcicegatheringstate + * See */ typedef enum /*< underscore_name=gst_webrtc_ice_gathering_state >*/ { @@ -88,15 +96,15 @@ typedef enum /*< underscore_name=gst_webrtc_ice_gathering_state >*/ /** * GstWebRTCICEConnectionState: - * GST_WEBRTC_ICE_CONNECTION_STATE_NEW: new - * GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING: checking - * GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED: connected - * GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED: completed - * GST_WEBRTC_ICE_CONNECTION_STATE_FAILED: failed - * GST_WEBRTC_ICE_CONNECTION_STATE_DISCONNECTED: disconnected - * GST_WEBRTC_ICE_CONNECTION_STATE_CLOSED: closed + * @GST_WEBRTC_ICE_CONNECTION_STATE_NEW: new + * @GST_WEBRTC_ICE_CONNECTION_STATE_CHECKING: checking + * @GST_WEBRTC_ICE_CONNECTION_STATE_CONNECTED: connected + * @GST_WEBRTC_ICE_CONNECTION_STATE_COMPLETED: completed + * @GST_WEBRTC_ICE_CONNECTION_STATE_FAILED: failed + * @GST_WEBRTC_ICE_CONNECTION_STATE_DISCONNECTED: disconnected + * @GST_WEBRTC_ICE_CONNECTION_STATE_CLOSED: closed * - * See http://w3c.github.io/webrtc-pc/#dom-rtciceconnectionstate + * See */ typedef enum /*< underscore_name=gst_webrtc_ice_connection_state >*/ { @@ -111,14 +119,14 @@ typedef enum /*< underscore_name=gst_webrtc_ice_connection_state >*/ /** * GstWebRTCSignalingState: - * GST_WEBRTC_SIGNALING_STATE_STABLE: stable - * GST_WEBRTC_SIGNALING_STATE_CLOSED: closed - * GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER: have-local-offer - * GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER: have-remote-offer - * GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER: have-local-pranswer - * GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER: have-remote-pranswer + * @GST_WEBRTC_SIGNALING_STATE_STABLE: stable + * @GST_WEBRTC_SIGNALING_STATE_CLOSED: closed + * @GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_OFFER: have-local-offer + * @GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER: have-remote-offer + * @GST_WEBRTC_SIGNALING_STATE_HAVE_LOCAL_PRANSWER: have-local-pranswer + * @GST_WEBRTC_SIGNALING_STATE_HAVE_REMOTE_PRANSWER: have-remote-pranswer * - * See http://w3c.github.io/webrtc-pc/#dom-rtcsignalingstate + * See */ typedef enum /*< underscore_name=gst_webrtc_signaling_state >*/ { @@ -132,14 +140,14 @@ typedef enum /*< underscore_name=gst_webrtc_signaling_state >*/ /** * GstWebRTCPeerConnectionState: - * GST_WEBRTC_PEER_CONNECTION_STATE_NEW: new - * GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTING: connecting - * GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTED: connected - * GST_WEBRTC_PEER_CONNECTION_STATE_DISCONNECTED: disconnected - * GST_WEBRTC_PEER_CONNECTION_STATE_FAILED: failed - * GST_WEBRTC_PEER_CONNECTION_STATE_CLOSED: closed + * @GST_WEBRTC_PEER_CONNECTION_STATE_NEW: new + * @GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTING: connecting + * @GST_WEBRTC_PEER_CONNECTION_STATE_CONNECTED: connected + * @GST_WEBRTC_PEER_CONNECTION_STATE_DISCONNECTED: disconnected + * @GST_WEBRTC_PEER_CONNECTION_STATE_FAILED: failed + * @GST_WEBRTC_PEER_CONNECTION_STATE_CLOSED: closed * - * See http://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectionstate + * See */ typedef enum /*< underscore_name=gst_webrtc_peer_connection_state >*/ { @@ -153,8 +161,8 @@ typedef enum /*< underscore_name=gst_webrtc_peer_connection_state >*/ /** * GstWebRTCICERole: - * GST_WEBRTC_ICE_ROLE_CONTROLLED: controlled - * GST_WEBRTC_ICE_ROLE_CONTROLLING: controlling + * @GST_WEBRTC_ICE_ROLE_CONTROLLED: controlled + * @GST_WEBRTC_ICE_ROLE_CONTROLLING: controlling */ typedef enum /*< underscore_name=gst_webrtc_ice_role >*/ { @@ -164,8 +172,8 @@ typedef enum /*< underscore_name=gst_webrtc_ice_role >*/ /** * GstWebRTCICEComponent: - * GST_WEBRTC_ICE_COMPONENT_RTP, - * GST_WEBRTC_ICE_COMPONENT_RTCP, + * @GST_WEBRTC_ICE_COMPONENT_RTP: RTP component + * @GST_WEBRTC_ICE_COMPONENT_RTCP: RTCP component */ typedef enum /*< underscore_name=gst_webrtc_ice_component >*/ { @@ -175,12 +183,12 @@ typedef enum /*< underscore_name=gst_webrtc_ice_component >*/ /** * GstWebRTCSDPType: - * GST_WEBRTC_SDP_TYPE_OFFER: offer - * GST_WEBRTC_SDP_TYPE_PRANSWER: pranswer - * GST_WEBRTC_SDP_TYPE_ANSWER: answer - * GST_WEBRTC_SDP_TYPE_ROLLBACK: rollback + * @GST_WEBRTC_SDP_TYPE_OFFER: offer + * @GST_WEBRTC_SDP_TYPE_PRANSWER: pranswer + * @GST_WEBRTC_SDP_TYPE_ANSWER: answer + * @GST_WEBRTC_SDP_TYPE_ROLLBACK: rollback * - * See http://w3c.github.io/webrtc-pc/#rtcsdptype + * See */ typedef enum /*< underscore_name=gst_webrtc_sdp_type >*/ { @@ -191,12 +199,12 @@ typedef enum /*< underscore_name=gst_webrtc_sdp_type >*/ } GstWebRTCSDPType; /** - * GstWebRTCRtpTransceiverDirection: - * GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE: none - * GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE: inactive - * GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY: sendonly - * GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY: recvonly - * GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV: sendrecv + * GstWebRTCRTPTransceiverDirection: + * @GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_NONE: none + * @GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_INACTIVE: inactive + * @GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDONLY: sendonly + * @GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY: recvonly + * @GST_WEBRTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV: sendrecv */ typedef enum /*< underscore_name=gst_webrtc_rtp_transceiver_direction >*/ { @@ -209,10 +217,10 @@ typedef enum /*< underscore_name=gst_webrtc_rtp_transceiver_direction >*/ /** * GstWebRTCDTLSSetup: - * GST_WEBRTC_DTLS_SETUP_NONE: none - * GST_WEBRTC_DTLS_SETUP_ACTPASS: actpass - * GST_WEBRTC_DTLS_SETUP_ACTIVE: sendonly - * GST_WEBRTC_DTLS_SETUP_PASSIVE: recvonly + * @GST_WEBRTC_DTLS_SETUP_NONE: none + * @GST_WEBRTC_DTLS_SETUP_ACTPASS: actpass + * @GST_WEBRTC_DTLS_SETUP_ACTIVE: sendonly + * @GST_WEBRTC_DTLS_SETUP_PASSIVE: recvonly */ typedef enum /*< underscore_name=gst_webrtc_dtls_setup >*/ { @@ -224,20 +232,20 @@ typedef enum /*< underscore_name=gst_webrtc_dtls_setup >*/ /** * GstWebRTCStatsType: - * GST_WEBRTC_STATS_CODEC: codec - * GST_WEBRTC_STATS_INBOUND_RTP: inbound-rtp - * GST_WEBRTC_STATS_OUTBOUND_RTP: outbound-rtp - * GST_WEBRTC_STATS_REMOTE_INBOUND_RTP: remote-inbound-rtp - * GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP: remote-outbound-rtp - * GST_WEBRTC_STATS_CSRC: csrc - * GST_WEBRTC_STATS_PEER_CONNECTION: peer-connectiion - * GST_WEBRTC_STATS_DATA_CHANNEL: data-channel - * GST_WEBRTC_STATS_STREAM: stream - * GST_WEBRTC_STATS_TRANSPORT: transport - * GST_WEBRTC_STATS_CANDIDATE_PAIR: candidate-pair - * GST_WEBRTC_STATS_LOCAL_CANDIDATE: local-candidate - * GST_WEBRTC_STATS_REMOTE_CANDIDATE: remote-candidate - * GST_WEBRTC_STATS_CERTIFICATE: certificate + * @GST_WEBRTC_STATS_CODEC: codec + * @GST_WEBRTC_STATS_INBOUND_RTP: inbound-rtp + * @GST_WEBRTC_STATS_OUTBOUND_RTP: outbound-rtp + * @GST_WEBRTC_STATS_REMOTE_INBOUND_RTP: remote-inbound-rtp + * @GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP: remote-outbound-rtp + * @GST_WEBRTC_STATS_CSRC: csrc + * @GST_WEBRTC_STATS_PEER_CONNECTION: peer-connectiion + * @GST_WEBRTC_STATS_DATA_CHANNEL: data-channel + * @GST_WEBRTC_STATS_STREAM: stream + * @GST_WEBRTC_STATS_TRANSPORT: transport + * @GST_WEBRTC_STATS_CANDIDATE_PAIR: candidate-pair + * @GST_WEBRTC_STATS_LOCAL_CANDIDATE: local-candidate + * @GST_WEBRTC_STATS_REMOTE_CANDIDATE: remote-candidate + * @GST_WEBRTC_STATS_CERTIFICATE: certificate */ typedef enum /*< underscore_name=gst_webrtc_stats_type >*/ { @@ -277,7 +285,7 @@ typedef enum /*< underscore_name=gst_webrtc_fec_type >*/ * GST_WEBRTC_SCTP_TRANSPORT_STATE_CONNECTED: connected * GST_WEBRTC_SCTP_TRANSPORT_STATE_CLOSED: closed * - * See http://w3c.github.io/webrtc-pc/#dom-rtcsctptransportstate + * See * * Since: 1.16 */ @@ -296,7 +304,7 @@ typedef enum /*< underscore_name=gst_webrtc_sctp_transport_state >*/ * GST_WEBRTC_PRIORITY_TYPE_MEDIUM: medium * GST_WEBRTC_PRIORITY_TYPE_HIGH: high * - * See http://w3c.github.io/webrtc-pc/#dom-rtcprioritytype + * See * * Since: 1.16 */ @@ -316,7 +324,7 @@ typedef enum /*< underscore_name=gst_webrtc_priority_type >*/ * GST_WEBRTC_DATA_CHANNEL_STATE_CLOSING: closing * GST_WEBRTC_DATA_CHANNEL_STATE_CLOSED: closed * - * See http://w3c.github.io/webrtc-pc/#dom-rtcdatachannelstate + * See * * Since: 1.16 */ diff --git a/packaging/gst-plugins-bad.spec b/packaging/gst-plugins-bad.spec index 035ff023b..f4ae56a10 100644 --- a/packaging/gst-plugins-bad.spec +++ b/packaging/gst-plugins-bad.spec @@ -87,6 +87,8 @@ export CFLAGS+=" -Wall -g -fPIC\ -DTIZEN_FEATURE_H264PARSE_MODIFICATION\ -DTIZEN_FEATURE_AD\ -DTIZEN_FEATURE_HLSDEMUX_LANG_TAG\ + -DTIZEN_FEATURE_UPSTREAM\ + -DTIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK\ -D__TIZEN__\ -fstack-protector-strong\ -Wl,-z,relro\ -- 2.34.1