Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
WebRTC: DTLS quick connection (#37)
Browse files Browse the repository at this point in the history
## What is the current behavior you want to change?
Current Kurento WebRTC connection introduces unneeded time to complete connection when using STUN or TURN candidates. 

DTLS is started by the peer acting as client (with property "is-client" to TRUE) sending an initial DTLS Hello packet. That packet should be responded by the other peer and exchange of keys will happen. If the other peer does not respond, the client will wait for an amount of time before resending DTLS Hello packet. That amount of time starts in 1 second and doubles on each retry, so the retries comes at 1 second, then 2 seconds, then 4, and so on.

In KMS the DTLS connection is managed by the KmsWebrtcTransportSink component, more specifically by the embbeded dtlssrtpenc component. When instantiated in a KmsWebrtcTransportSinkNice component, the sending is managed by a nicesink element associated to the niceagent of the connection. 

The problem appears because dtlssrtpenc element initiates DTLS connection as soon as it reaches the PAUSED or PLAYING state, and this always happens before the ICE negotiation has reached to a first valid candidate pair, at least when no HOST valid candidates are found. When this happens, the first DTLS Hello packet is silently dropped by the nicesink as there is no ICE connection established yet. And the timeout for next DTLS Hello starts to run. 

The outcome is that when using STUN or TURN candidates, once a first valid pair is found, one or more DTLS Hello packets have already been sent, and as the timeout doubles each retry, it is likely that after ICE connection is established waiting for next retry will take a similar time to the what it took to establish ICE connection.


## What is the new behavior provided by this change?
This change consist on the following modifications:
* To prevent dtlssrtpenc to trigger DTLS initiation, we lock its state to NULL until we know for sure that it is ready to work, that is, until we know the peer is acting as server, or, if the peer is acting as client, until ICE gets to a CONNECTED state. At those points we unlock dtlssrtpenc state and synchronize with its parent. In fact, to impact less possible elements it is not the dtlssrtpenc component but the dtlsenc embbeded on it
* We added a virtual method to the KmsWebrtcTransportSink to set the is-client property, to allow the KmsWebrtcTransportSinkNice to decide when to start DTLS. This must be taken into account for potential future KmsWebrtcTransportSink derived classes
* We added a method to start DTLS connection. This is used on KmsWebrtcTransportSinkNice  as soon as the sink knows it is server, or if it is client, as soon as ICE gets to a CONNECTED state
* On KmsWebrtcTransportSinkNice configure, we subscribe to the ICE connection event to know when ICE connection reaches to CONNECTED state.
  • Loading branch information
slabajo authored Apr 28, 2022
1 parent b05bc3f commit d1769dc
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/gst-plugins/webrtcendpoint/kmswebrtcbundleconnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ kms_webrtc_bundle_connection_add (KmsIRtpConnection * base_rtp_conn,
KmsWebRtcBundleConnectionPrivate *priv = self->priv;
KmsWebRtcTransport *tr = priv->tr;

g_object_set (G_OBJECT (tr->sink->dtlssrtpenc), "is-client", active, NULL);
kms_webrtc_transport_sink_set_dtls_is_client (tr->sink, active);

gst_bin_add (bin, GST_ELEMENT (g_object_ref (tr->src)));
gst_bin_add (bin, GST_ELEMENT (g_object_ref (tr->sink)));
Expand Down
2 changes: 1 addition & 1 deletion src/gst-plugins/webrtcendpoint/kmswebrtcconnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ kms_webrtc_connection_get_certificate_pem (KmsWebRtcBaseConnection * base_conn)
static void
add_tr (KmsWebRtcTransport * tr, GstBin * bin, gboolean is_client)
{
g_object_set (G_OBJECT (tr->sink->dtlssrtpenc), "is-client", is_client, NULL);
kms_webrtc_transport_sink_set_dtls_is_client(tr->sink, is_client);

gst_bin_add (bin, GST_ELEMENT (g_object_ref (tr->src)));
gst_bin_add (bin, GST_ELEMENT (g_object_ref (tr->sink)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ kms_webrtc_rtcp_mux_connection_add (KmsIRtpConnection * base_rtp_conn,
KmsWebRtcTransport *tr = priv->tr;

/* srcs */
g_object_set (G_OBJECT (tr->sink->dtlssrtpenc), "is-client", active, NULL);
kms_webrtc_transport_sink_set_dtls_is_client(tr->sink, active);

gst_bin_add (bin, GST_ELEMENT (g_object_ref (tr->src)));
gst_bin_add (bin, GST_ELEMENT (g_object_ref (tr->sink)));
Expand Down
2 changes: 1 addition & 1 deletion src/gst-plugins/webrtcendpoint/kmswebrtcsctpconnection.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ kms_webrtc_sctp_connection_add (KmsIRtpConnection * base_conn, GstBin * bin,
KmsWebRtcSctpConnectionPrivate *priv = self->priv;
KmsWebRtcTransport *tr = priv->tr;

g_object_set (G_OBJECT (tr->sink->dtlssrtpenc), "is-client", active, NULL);
kms_webrtc_transport_sink_set_dtls_is_client(tr->sink, active);

gst_bin_add (bin, GST_ELEMENT (g_object_ref (self->priv->tr->src)));
gst_bin_add (bin, GST_ELEMENT (g_object_ref (self->priv->tr->sink)));
Expand Down
104 changes: 97 additions & 7 deletions src/gst-plugins/webrtcendpoint/kmswebrtctransportsink.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,39 +29,88 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define kms_webrtc_transport_sink_parent_class parent_class
G_DEFINE_TYPE (KmsWebrtcTransportSink, kms_webrtc_transport_sink, GST_TYPE_BIN);

#define FUNNEL_NAME "funnel"
#define SRTPENC_NAME "srtp-encoder"
#define FUNNEL_FACTORY_NAME "funnel"
#define SRTPENC_FACTORY_NAME "srtpenc"
#define DTLS_ENCODER_FACTORY_NAME "dtlsenc"



static GstElement*
kms_webrtc_transport_sink_get_element_in_dtlssrtpenc (KmsWebrtcTransportSink *self, const gchar *factory_name)
{
GstIterator *iterator;
GValue item = G_VALUE_INIT;
GstElement *element;
GstElementFactory *factory;

factory = gst_element_factory_find (factory_name);

if (factory == NULL) {
GST_WARNING_OBJECT(self, "Factory %s not installed", factory_name);
return NULL;
}

// Until KMS is updated to GStreamer 1.18 and method https://gstreamer.freedesktop.org/documentation/gstreamer/gstbin.html#gst_bin_iterate_all_by_element_factory_name
// is available, this will do
iterator = gst_bin_iterate_elements (GST_BIN(self->dtlssrtpenc));
while (gst_iterator_next (iterator, &item) == GST_ITERATOR_OK) {
element = (GstElement *) g_value_get_object (&item);
if (factory == gst_element_get_factory (element)) {
break;
} else {
element = NULL;
}
}
gst_iterator_free (iterator);
g_object_unref (factory);

if (element != NULL) {
element = g_value_dup_object (&item);
g_value_unset (&item);
}
return element;
}

static void
kms_webrtc_transport_sink_init (KmsWebrtcTransportSink * self)
{
GstElement *dtls_encoder;

self->dtlssrtpenc = gst_element_factory_make ("dtlssrtpenc", NULL);
dtls_encoder = kms_webrtc_transport_sink_get_element_in_dtlssrtpenc (self, DTLS_ENCODER_FACTORY_NAME);
if (dtls_encoder != NULL) {
gst_element_set_locked_state (dtls_encoder, TRUE);
g_object_unref (dtls_encoder);
} else {
GST_WARNING_OBJECT (self, "Cannot get DTLS encoder");
}
}

void
kms_webrtc_transport_sink_connect_elements (KmsWebrtcTransportSink * self)
kms_webrtc_transport_sink_connect_elements (KmsWebrtcTransportSink *self)
{
GstElement *funnel, *srtpenc;

gst_bin_add_many (GST_BIN (self), self->dtlssrtpenc, self->sink, NULL);
gst_element_link (self->dtlssrtpenc, self->sink);

funnel = gst_bin_get_by_name (GST_BIN (self->dtlssrtpenc), FUNNEL_NAME);
funnel = kms_webrtc_transport_sink_get_element_in_dtlssrtpenc (self, FUNNEL_FACTORY_NAME);
if (funnel != NULL) {
g_object_set (funnel, "forward-sticky-events-mode", 0 /* never */ , NULL);
g_object_unref (funnel);
} else {
GST_WARNING ("Cannot get funnel with name %s", FUNNEL_NAME);
GST_WARNING ("Cannot get funnel with factory %s", FUNNEL_FACTORY_NAME);
}

srtpenc = gst_bin_get_by_name (GST_BIN (self->dtlssrtpenc), SRTPENC_NAME);
srtpenc = kms_webrtc_transport_sink_get_element_in_dtlssrtpenc (self, SRTPENC_FACTORY_NAME);
if (srtpenc != NULL) {
g_object_set (srtpenc, "allow-repeat-tx", TRUE, "replay-window-size",
RTP_RTX_SIZE, NULL);
g_object_unref (srtpenc);
} else {
GST_WARNING ("Cannot get srtpenc with name %s", SRTPENC_NAME);
GST_WARNING ("Cannot get srtpenc with factory %s", SRTPENC_FACTORY_NAME);
}

}

void
Expand All @@ -77,6 +126,18 @@ kms_webrtc_transport_sink_configure_default (KmsWebrtcTransportSink * self,
}
}

void
kms_webrtc_transport_sink_set_dtls_is_client_default (KmsWebrtcTransportSink * self,
gboolean is_client)
{
g_object_set (G_OBJECT (self->dtlssrtpenc), "is-client", is_client, NULL);
if (is_client) {
GST_DEBUG_OBJECT(self, "Set as DTLS client (handshake initiator)");
} else {
GST_DEBUG_OBJECT(self, "Set as DTLS server (wait for handshake)");
}
}

void
kms_webrtc_transport_sink_configure (KmsWebrtcTransportSink * self,
KmsIceBaseAgent * agent, const char *stream_id, guint component_id)
Expand All @@ -87,12 +148,23 @@ kms_webrtc_transport_sink_configure (KmsWebrtcTransportSink * self,
klass->configure (self, agent, stream_id, component_id);
}

void
kms_webrtc_transport_sink_set_dtls_is_client (KmsWebrtcTransportSink * self,
gboolean is_client)
{
KmsWebrtcTransportSinkClass *klass =
KMS_WEBRTC_TRANSPORT_SINK_CLASS (G_OBJECT_GET_CLASS (self));

klass->set_dtls_is_client (self, is_client);
}

static void
kms_webrtc_transport_sink_class_init (KmsWebrtcTransportSinkClass * klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);

klass->configure = kms_webrtc_transport_sink_configure_default;
klass->set_dtls_is_client = kms_webrtc_transport_sink_set_dtls_is_client_default;

GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
GST_DEFAULT_NAME);
Expand All @@ -104,6 +176,24 @@ kms_webrtc_transport_sink_class_init (KmsWebrtcTransportSinkClass * klass)
"Miguel París Díaz <mparisdiaz@gmail.com>");
}

void
kms_webrtc_transport_sink_start_dtls (KmsWebrtcTransportSink * self)
{
GstElement *dtls_encoder;

dtls_encoder = kms_webrtc_transport_sink_get_element_in_dtlssrtpenc (self, DTLS_ENCODER_FACTORY_NAME);
if (dtls_encoder != NULL) {
gst_element_set_locked_state (dtls_encoder, FALSE);
gst_element_sync_state_with_parent (dtls_encoder);
GST_DEBUG_OBJECT(self, "Starting DTLS");

g_object_unref (dtls_encoder);
} else {
GST_WARNING_OBJECT ("Cannot get DTLS encoder with factory %s", DTLS_ENCODER_FACTORY_NAME);
}
}


KmsWebrtcTransportSink *
kms_webrtc_transport_sink_new ()
{
Expand Down
7 changes: 7 additions & 0 deletions src/gst-plugins/webrtcendpoint/kmswebrtctransportsink.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ struct _KmsWebrtcTransportSinkClass
KmsIceBaseAgent *agent,
const char *stream_id,
guint component_id);

void (*set_dtls_is_client) (KmsWebrtcTransportSink * self,
gboolean is_client);
};

GType kms_webrtc_transport_sink_get_type (void);
Expand All @@ -65,5 +68,9 @@ void kms_webrtc_transport_sink_configure (KmsWebrtcTransportSink * self,
KmsIceBaseAgent *agent,
const char *stream_id,
guint component_id);
void kms_webrtc_transport_sink_set_dtls_is_client (KmsWebrtcTransportSink * self,
gboolean is_client);
void kms_webrtc_transport_sink_start_dtls (KmsWebrtcTransportSink * self);

G_END_DECLS
#endif /* __KMS_WEBRTC_TRANSPORT_SINK_H__ */
40 changes: 40 additions & 0 deletions src/gst-plugins/webrtcendpoint/kmswebrtctransportsinknice.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ kms_webrtc_transport_sink_nice_init (KmsWebrtcTransportSinkNice * self)
kms_webrtc_transport_sink_connect_elements (parent);
}

static void
kms_webrtc_transport_sink_nice_component_state_changed (KmsIceBaseAgent * agent,
char *stream_id, guint component_id, IceState state,
KmsWebrtcTransportSink * self)
{
gboolean is_client;

GST_LOG_OBJECT (self,
"[IceComponentStateChanged] state: %s, stream_id: %s, component_id: %u",
kms_ice_base_agent_state_to_string (state), stream_id, component_id);

g_object_get (G_OBJECT (self->dtlssrtpenc), "is-client",
&is_client, NULL);

if ((state == ICE_STATE_CONNECTED) && is_client) {
kms_webrtc_transport_sink_start_dtls (self);
}
}

void
kms_webrtc_transport_sink_nice_configure (KmsWebrtcTransportSink * self,
KmsIceBaseAgent * agent, const char *stream_id, guint component_id)
Expand All @@ -53,6 +72,26 @@ kms_webrtc_transport_sink_nice_configure (KmsWebrtcTransportSink * self,
"agent", kms_ice_nice_agent_get_agent (nice_agent),
"stream", id, "component", component_id,
"sync", FALSE, "async", FALSE, NULL);

g_signal_connect (nice_agent, "on-ice-component-state-changed",
G_CALLBACK (kms_webrtc_transport_sink_nice_component_state_changed), self);
}

void
kms_webrtc_transport_sink_nice_set_dtls_is_client (KmsWebrtcTransportSink * self,
gboolean is_client)
{
KmsWebrtcTransportSinkNiceClass *klass =
KMS_WEBRTC_TRANSPORT_SINK_NICE_CLASS (G_OBJECT_GET_CLASS (self));
KmsWebrtcTransportSinkClass *parent_klass =
KMS_WEBRTC_TRANSPORT_SINK_CLASS (g_type_class_peek_parent(klass));

parent_klass->set_dtls_is_client (self, is_client);

if (!is_client) {
kms_webrtc_transport_sink_start_dtls (self);
}

}

static void
Expand All @@ -64,6 +103,7 @@ kms_webrtc_transport_sink_nice_class_init (KmsWebrtcTransportSinkNiceClass *

base_class = KMS_WEBRTC_TRANSPORT_SINK_CLASS (klass);
base_class->configure = kms_webrtc_transport_sink_nice_configure;
base_class->set_dtls_is_client = kms_webrtc_transport_sink_nice_set_dtls_is_client;

GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
GST_DEFAULT_NAME);
Expand Down
1 change: 1 addition & 0 deletions tests/check/element/webrtcendpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,7 @@ test_data_channels (gboolean bundle)

GST_WARNING ("Finishing test");

g_usleep (500000);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_bus_remove_signal_watch (bus);
g_object_unref (bus);
Expand Down
Loading

0 comments on commit d1769dc

Please sign in to comment.