webrtcbin: New add-turn-server API

It is possible and often desirable to pass multiple ICE relays
to libnice agents, the "turn-server" property, while convenient
to use from the command line, does not allow that.

This adds a new action signal, "add-turn-server" to address that.

https://bugzilla.gnome.org/show_bug.cgi?id=797012
This commit is contained in:
Mathieu Duponchelle 2018-08-22 19:05:02 +02:00
parent 29c7f95884
commit 1d6160d59c
3 changed files with 142 additions and 55 deletions

View file

@ -250,6 +250,7 @@ enum
GET_STATS_SIGNAL,
ADD_TRANSCEIVER_SIGNAL,
GET_TRANSCEIVERS_SIGNAL,
ADD_TURN_SERVER_SIGNAL,
LAST_SIGNAL,
};
@ -3299,6 +3300,17 @@ gst_webrtc_bin_get_transceivers (GstWebRTCBin * webrtc)
return arr;
}
static gboolean
gst_webrtc_bin_add_turn_server (GstWebRTCBin * webrtc, const gchar * uri)
{
g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
g_return_val_if_fail (uri != NULL, FALSE);
GST_DEBUG_OBJECT (webrtc, "Adding turn server: %s", uri);
return gst_webrtc_ice_add_turn_server (webrtc->priv->ice, uri);
}
static gboolean
copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
{
@ -4078,7 +4090,9 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass)
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",
"The TURN server of the form turn(s)://username:password@host:port. "
"This is a convenience property, use #GstWebRTCBin::add-turn-server "
"if you wish to use multiple TURN servers",
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
@ -4309,6 +4323,19 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * 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);
/**
* GstWebRTCBin::add-turn-server:
* @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
*/
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);
}
static void

View file

@ -32,6 +32,8 @@
* - are locally generated remote candidates meant to be readded to libnice?
*/
static GstUri *_validate_turn_server (GstWebRTCICE * ice, const gchar * s);
#define GST_CAT_DEFAULT gst_webrtc_ice_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@ -292,11 +294,76 @@ _parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass)
*pass = g_strdup (&colon[1]);
}
static void
_add_turn_server (GstWebRTCICE * ice, struct NiceStreamItem *item,
GstUri * turn_server)
{
gboolean ret;
gchar *user, *pass;
const gchar *userinfo, *transport, *scheme;
NiceRelayType relays[4] = { 0, };
int i, relay_n = 0;
scheme = gst_uri_get_scheme (turn_server);
transport = gst_uri_get_query_value (turn_server, "transport");
userinfo = gst_uri_get_userinfo (turn_server);
_parse_userinfo (userinfo, &user, &pass);
if (g_strcmp0 (scheme, "turns") == 0) {
relays[relay_n++] = NICE_RELAY_TYPE_TURN_TLS;
} else if (g_strcmp0 (scheme, "turn") == 0) {
if (!transport || g_strcmp0 (transport, "udp") == 0)
relays[relay_n++] = NICE_RELAY_TYPE_TURN_UDP;
if (!transport || g_strcmp0 (transport, "tcp") == 0)
relays[relay_n++] = NICE_RELAY_TYPE_TURN_TCP;
}
g_assert (relay_n < G_N_ELEMENTS (relays));
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]);
if (!ret) {
gchar *uri = gst_uri_to_string (turn_server);
GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
g_free (uri);
break;
}
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]);
if (!ret) {
gchar *uri = gst_uri_to_string (turn_server);
GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
g_free (uri);
break;
}
}
g_free (user);
g_free (pass);
}
typedef struct
{
GstWebRTCICE *ice;
struct NiceStreamItem *item;
} AddTurnServerData;
static void
_add_turn_server_func (const gchar * uri, GstUri * turn_server,
AddTurnServerData * data)
{
_add_turn_server (data->ice, data->item, turn_server);
}
GstWebRTCICEStream *
gst_webrtc_ice_add_stream (GstWebRTCICE * ice, guint session_id)
{
struct NiceStreamItem m = NICE_MATCH_INIT;
struct NiceStreamItem *item;
AddTurnServerData add_data;
m.session_id = session_id;
item = _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
@ -309,53 +376,15 @@ gst_webrtc_ice_add_stream (GstWebRTCICE * ice, guint session_id)
item = _create_nice_stream_item (ice, session_id);
if (ice->turn_server) {
gboolean ret;
gchar *user, *pass;
const gchar *userinfo, *transport, *scheme;
NiceRelayType relays[4] = { 0, };
int i, relay_n = 0;
scheme = gst_uri_get_scheme (ice->turn_server);
transport = gst_uri_get_query_value (ice->turn_server, "transport");
userinfo = gst_uri_get_userinfo (ice->turn_server);
_parse_userinfo (userinfo, &user, &pass);
if (g_strcmp0 (scheme, "turns") == 0) {
relays[relay_n++] = NICE_RELAY_TYPE_TURN_TLS;
} else if (g_strcmp0 (scheme, "turn") == 0) {
if (!transport || g_strcmp0 (transport, "udp") == 0)
relays[relay_n++] = NICE_RELAY_TYPE_TURN_UDP;
if (!transport || g_strcmp0 (transport, "tcp") == 0)
relays[relay_n++] = NICE_RELAY_TYPE_TURN_TCP;
}
g_assert (relay_n < G_N_ELEMENTS (relays));
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 (ice->turn_server),
gst_uri_get_port (ice->turn_server), user, pass, relays[i]);
if (!ret) {
gchar *uri = gst_uri_to_string (ice->turn_server);
GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
g_free (uri);
break;
}
ret = nice_agent_set_relay_info (ice->priv->nice_agent,
item->nice_stream_id, NICE_COMPONENT_TYPE_RTCP,
gst_uri_get_host (ice->turn_server),
gst_uri_get_port (ice->turn_server), user, pass, relays[i]);
if (!ret) {
gchar *uri = gst_uri_to_string (ice->turn_server);
GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
g_free (uri);
break;
}
}
g_free (user);
g_free (pass);
_add_turn_server (ice, item, ice->turn_server);
}
add_data.ice = ice;
add_data.item = item;
g_hash_table_foreach (ice->turn_servers, (GHFunc) _add_turn_server_func,
&add_data);
return item->stream;
}
@ -532,6 +561,23 @@ gst_webrtc_ice_set_remote_credentials (GstWebRTCICE * ice,
return TRUE;
}
gboolean
gst_webrtc_ice_add_turn_server (GstWebRTCICE * ice, const gchar * uri)
{
gboolean ret = FALSE;
GstUri *valid_uri;
if (!(valid_uri = _validate_turn_server (ice, uri)))
goto done;
g_hash_table_insert (ice->turn_servers, g_strdup (uri), valid_uri);
ret = TRUE;
done:
return ret;
}
gboolean
gst_webrtc_ice_set_local_credentials (GstWebRTCICE * ice,
GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
@ -600,8 +646,8 @@ _resolve_host (const gchar * host)
return g_inet_address_to_string (addr);
}
static void
_set_turn_server (GstWebRTCICE * ice, const gchar * s)
static GstUri *
_validate_turn_server (GstWebRTCICE * ice, const gchar * s)
{
GstUri *uri = gst_uri_from_string (s);
const gchar *userinfo, *host, *scheme;
@ -610,11 +656,11 @@ _set_turn_server (GstWebRTCICE * ice, const gchar * s)
gboolean turn_tls = FALSE;
guint port;
GST_DEBUG_OBJECT (ice, "setting turn server, %s", s);
GST_DEBUG_OBJECT (ice, "validating turn server, %s", s);
if (!uri) {
GST_ERROR_OBJECT (ice, "Could not parse turn server '%s'", s);
return;
return NULL;
}
scheme = gst_uri_get_scheme (uri);
@ -679,15 +725,13 @@ _set_turn_server (GstWebRTCICE * ice, const gchar * s)
/* Set the resolved IP as the host since that's what libnice wants */
gst_uri_set_host (uri, ip);
if (ice->turn_server)
gst_uri_unref (ice->turn_server);
ice->turn_server = uri;
out:
g_list_free (keys);
g_free (ip);
g_free (user);
g_free (pass);
return uri;
}
static void
@ -741,7 +785,13 @@ gst_webrtc_ice_set_property (GObject * object, guint prop_id,
break;
}
case PROP_TURN_SERVER:{
_set_turn_server (ice, g_value_get_string (value));
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:
@ -807,6 +857,8 @@ gst_webrtc_ice_finalize (GObject * object)
g_object_unref (ice->priv->nice_agent);
g_hash_table_unref (ice->turn_servers);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -863,6 +915,10 @@ gst_webrtc_ice_init (GstWebRTCICE * ice)
g_mutex_init (&ice->priv->lock);
g_cond_init (&ice->priv->cond);
ice->turn_servers =
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,

View file

@ -48,6 +48,8 @@ struct _GstWebRTCICE
GstUri *stun_server;
GstUri *turn_server;
GHashTable *turn_servers;
GstWebRTCICEPrivate *priv;
};
@ -77,6 +79,8 @@ gboolean gst_webrtc_ice_set_remote_credentials (GstWebRTCIC
GstWebRTCICEStream * stream,
gchar * ufrag,
gchar * pwd);
gboolean gst_webrtc_ice_add_turn_server (GstWebRTCICE * ice,
const gchar * uri);
G_END_DECLS