2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "gstwebrtcice.h"
27 #include "icestream.h"
28 #include "nicetransport.h"
32 * - are locally generated remote candidates meant to be readded to libnice?
35 static GstUri *_validate_turn_server (GstWebRTCICE * ice, const gchar * s);
37 #define GST_CAT_DEFAULT gst_webrtc_ice_debug
38 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
41 gst_webrtc_ice_error_quark (void)
43 return g_quark_from_static_string ("gst-webrtc-ice-error-quark");
49 ADD_LOCAL_IP_ADDRESS_SIGNAL,
61 static guint gst_webrtc_ice_signals[LAST_SIGNAL] = { 0 };
63 struct _GstWebRTCICEPrivate
65 NiceAgent *nice_agent;
67 GArray *nice_stream_map;
70 GMainContext *main_context;
75 GstWebRTCIceOnCandidateFunc on_candidate;
76 gpointer on_candidate_data;
77 GDestroyNotify on_candidate_notify;
80 #define gst_webrtc_ice_parent_class parent_class
81 G_DEFINE_TYPE_WITH_CODE (GstWebRTCICE, gst_webrtc_ice,
82 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstWebRTCICE)
83 GST_DEBUG_CATEGORY_INIT (gst_webrtc_ice_debug, "webrtcice", 0,
87 _unlock_pc_thread (GMutex * lock)
89 g_mutex_unlock (lock);
90 return G_SOURCE_REMOVE;
94 _gst_nice_thread (GstWebRTCICE * ice)
96 g_mutex_lock (&ice->priv->lock);
97 ice->priv->main_context = g_main_context_new ();
98 ice->priv->loop = g_main_loop_new (ice->priv->main_context, FALSE);
100 g_cond_broadcast (&ice->priv->cond);
101 g_main_context_invoke (ice->priv->main_context,
102 (GSourceFunc) _unlock_pc_thread, &ice->priv->lock);
104 g_main_loop_run (ice->priv->loop);
106 g_mutex_lock (&ice->priv->lock);
107 g_main_context_unref (ice->priv->main_context);
108 ice->priv->main_context = NULL;
109 g_main_loop_unref (ice->priv->loop);
110 ice->priv->loop = NULL;
111 g_cond_broadcast (&ice->priv->cond);
112 g_mutex_unlock (&ice->priv->lock);
118 _start_thread (GstWebRTCICE * ice)
120 g_mutex_lock (&ice->priv->lock);
121 ice->priv->thread = g_thread_new (GST_OBJECT_NAME (ice),
122 (GThreadFunc) _gst_nice_thread, ice);
124 while (!ice->priv->loop)
125 g_cond_wait (&ice->priv->cond, &ice->priv->lock);
126 g_mutex_unlock (&ice->priv->lock);
130 _stop_thread (GstWebRTCICE * ice)
132 g_mutex_lock (&ice->priv->lock);
133 g_main_loop_quit (ice->priv->loop);
134 while (ice->priv->loop)
135 g_cond_wait (&ice->priv->cond, &ice->priv->lock);
136 g_mutex_unlock (&ice->priv->lock);
138 g_thread_unref (ice->priv->thread);
142 static NiceComponentType
143 _webrtc_component_to_nice (GstWebRTCICEComponent comp)
146 case GST_WEBRTC_ICE_COMPONENT_RTP:
147 return NICE_COMPONENT_TYPE_RTP;
148 case GST_WEBRTC_ICE_COMPONENT_RTCP:
149 return NICE_COMPONENT_TYPE_RTCP;
151 g_assert_not_reached ();
156 static GstWebRTCICEComponent
157 _nice_component_to_webrtc (NiceComponentType comp)
160 case NICE_COMPONENT_TYPE_RTP:
161 return GST_WEBRTC_ICE_COMPONENT_RTP;
162 case NICE_COMPONENT_TYPE_RTCP:
163 return GST_WEBRTC_ICE_COMPONENT_RTCP;
165 g_assert_not_reached ();
170 struct NiceStreamItem
173 guint nice_stream_id;
174 GstWebRTCICEStream *stream;
177 /* TRUE to continue, FALSE to stop */
178 typedef gboolean (*NiceStreamItemForeachFunc) (struct NiceStreamItem * item,
182 _nice_stream_item_foreach (GstWebRTCICE * ice, NiceStreamItemForeachFunc func,
187 len = ice->priv->nice_stream_map->len;
188 for (i = 0; i < len; i++) {
189 struct NiceStreamItem *item =
190 &g_array_index (ice->priv->nice_stream_map, struct NiceStreamItem,
193 if (!func (item, data))
198 /* TRUE for match, FALSE otherwise */
199 typedef gboolean (*NiceStreamItemFindFunc) (struct NiceStreamItem * item,
204 NiceStreamItemFindFunc func;
206 struct NiceStreamItem *ret;
210 _find_nice_item (struct NiceStreamItem *item, gpointer user_data)
212 struct nice_find *f = user_data;
213 if (f->func (item, f->data)) {
220 static struct NiceStreamItem *
221 _nice_stream_item_find (GstWebRTCICE * ice, NiceStreamItemFindFunc func,
230 _nice_stream_item_foreach (ice, _find_nice_item, &f);
235 #define NICE_MATCH_INIT { -1, -1, NULL }
238 _match (struct NiceStreamItem *item, struct NiceStreamItem *m)
240 if (m->session_id != -1 && m->session_id != item->session_id)
242 if (m->nice_stream_id != -1 && m->nice_stream_id != item->nice_stream_id)
244 if (m->stream != NULL && m->stream != item->stream)
250 static struct NiceStreamItem *
251 _find_item (GstWebRTCICE * ice, guint session_id, guint nice_stream_id,
252 GstWebRTCICEStream * stream)
254 struct NiceStreamItem m = NICE_MATCH_INIT;
256 m.session_id = session_id;
257 m.nice_stream_id = nice_stream_id;
260 return _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
263 static struct NiceStreamItem *
264 _create_nice_stream_item (GstWebRTCICE * ice, guint session_id)
266 struct NiceStreamItem item;
268 item.session_id = session_id;
269 item.nice_stream_id = nice_agent_add_stream (ice->priv->nice_agent, 2);
270 item.stream = gst_webrtc_ice_stream_new (ice, item.nice_stream_id);
271 g_array_append_val (ice->priv->nice_stream_map, item);
273 return _find_item (ice, item.session_id, item.nice_stream_id, item.stream);
277 _parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass)
287 colon = g_strstr_len (userinfo, -1, ":");
289 *user = g_strdup (userinfo);
294 /* Check that the first occurence is also the last occurence */
295 if (colon != g_strrstr (userinfo, ":"))
296 GST_WARNING ("userinfo %s contains more than one ':', will assume that the "
297 "first ':' delineates user:pass. You should escape the user and pass "
298 "before adding to the URI.", userinfo);
300 *user = g_strndup (userinfo, colon - userinfo);
301 *pass = g_strdup (&colon[1]);
305 _resolve_host (GstWebRTCICE * ice, const gchar * host)
307 GResolver *resolver = g_resolver_get_default ();
308 GError *error = NULL;
313 GST_DEBUG_OBJECT (ice, "Resolving host %s", host);
315 if (!(addresses = g_resolver_lookup_by_name (resolver, host, NULL, &error))) {
316 GST_ERROR ("%s", error->message);
317 g_clear_error (&error);
321 GST_DEBUG_OBJECT (ice, "Resolved %d addresses for host %s",
322 g_list_length (addresses), host);
324 /* XXX: only the first address is used */
325 addr = addresses->data;
326 address = g_inet_address_to_string (addr);
327 g_resolver_free_addresses (addresses);
333 _add_turn_server (GstWebRTCICE * ice, struct NiceStreamItem *item,
334 GstUri * turn_server)
338 const gchar *host, *userinfo, *transport, *scheme;
339 NiceRelayType relays[4] = { 0, };
343 host = gst_uri_get_host (turn_server);
345 GST_ERROR_OBJECT (ice, "Turn server has no host");
348 ip = _resolve_host (ice, host);
350 GST_ERROR_OBJECT (ice, "Failed to resolve turn server '%s'", host);
354 /* Set the resolved IP as the host since that's what libnice wants */
355 gst_uri_set_host (turn_server, ip);
357 scheme = gst_uri_get_scheme (turn_server);
358 transport = gst_uri_get_query_value (turn_server, "transport");
359 userinfo = gst_uri_get_userinfo (turn_server);
360 _parse_userinfo (userinfo, &user, &pass);
362 if (g_strcmp0 (scheme, "turns") == 0) {
363 relays[relay_n++] = NICE_RELAY_TYPE_TURN_TLS;
364 } else if (g_strcmp0 (scheme, "turn") == 0) {
365 if (!transport || g_strcmp0 (transport, "udp") == 0)
366 relays[relay_n++] = NICE_RELAY_TYPE_TURN_UDP;
367 if (!transport || g_strcmp0 (transport, "tcp") == 0)
368 relays[relay_n++] = NICE_RELAY_TYPE_TURN_TCP;
370 g_assert (relay_n < G_N_ELEMENTS (relays));
372 for (i = 0; i < relay_n; i++) {
373 ret = nice_agent_set_relay_info (ice->priv->nice_agent,
374 item->nice_stream_id, NICE_COMPONENT_TYPE_RTP,
375 gst_uri_get_host (turn_server), gst_uri_get_port (turn_server), user,
378 gchar *uri = gst_uri_to_string (turn_server);
379 GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
383 ret = nice_agent_set_relay_info (ice->priv->nice_agent,
384 item->nice_stream_id, NICE_COMPONENT_TYPE_RTCP,
385 gst_uri_get_host (turn_server), gst_uri_get_port (turn_server), user,
388 gchar *uri = gst_uri_to_string (turn_server);
389 GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
404 struct NiceStreamItem *item;
408 _add_turn_server_func (const gchar * uri, GstUri * turn_server,
409 AddTurnServerData * data)
411 _add_turn_server (data->ice, data->item, turn_server);
415 _add_stun_server (GstWebRTCICE * ice, GstUri * stun_server)
417 const gchar *msg = "must be of the form stun://<host>:<port>";
423 s = gst_uri_to_string (stun_server);
424 GST_DEBUG_OBJECT (ice, "adding stun server, %s", s);
426 host = gst_uri_get_host (stun_server);
428 GST_ERROR_OBJECT (ice, "Stun server '%s' has no host, %s", s, msg);
432 port = gst_uri_get_port (stun_server);
433 if (port == GST_URI_NO_PORT) {
434 GST_INFO_OBJECT (ice, "Stun server '%s' has no port, assuming 3478", s);
436 gst_uri_set_port (stun_server, port);
439 ip = _resolve_host (ice, host);
441 GST_ERROR_OBJECT (ice, "Failed to resolve stun server '%s'", host);
445 g_object_set (ice->priv->nice_agent, "stun-server", ip,
446 "stun-server-port", port, NULL);
454 gst_webrtc_ice_add_stream (GstWebRTCICE * ice, guint session_id)
456 struct NiceStreamItem m = NICE_MATCH_INIT;
457 struct NiceStreamItem *item;
458 AddTurnServerData add_data;
460 m.session_id = session_id;
461 item = _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
463 GST_ERROR_OBJECT (ice, "stream already added with session_id=%u",
468 if (ice->stun_server) {
469 _add_stun_server (ice, ice->stun_server);
472 item = _create_nice_stream_item (ice, session_id);
474 if (ice->turn_server) {
475 _add_turn_server (ice, item, ice->turn_server);
479 add_data.item = item;
481 g_hash_table_foreach (ice->turn_servers, (GHFunc) _add_turn_server_func,
488 _on_new_candidate (NiceAgent * agent, NiceCandidate * candidate,
491 struct NiceStreamItem *item;
494 item = _find_item (ice, -1, candidate->stream_id, NULL);
496 GST_WARNING_OBJECT (ice, "received signal for non-existent stream %u",
497 candidate->stream_id);
501 if (!candidate->username || !candidate->password) {
502 gboolean got_credentials;
503 gchar *ufrag, *password;
505 got_credentials = nice_agent_get_local_credentials (ice->priv->nice_agent,
506 candidate->stream_id, &ufrag, &password);
507 g_warn_if_fail (got_credentials);
509 if (!candidate->username)
510 candidate->username = ufrag;
514 if (!candidate->password)
515 candidate->password = password;
520 attr = nice_agent_generate_local_candidate_sdp (agent, candidate);
522 if (ice->priv->on_candidate)
523 ice->priv->on_candidate (ice, item->session_id, attr,
524 ice->priv->on_candidate_data);
529 GstWebRTCICETransport *
530 gst_webrtc_ice_find_transport (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
531 GstWebRTCICEComponent component)
533 struct NiceStreamItem *item;
535 item = _find_item (ice, -1, -1, stream);
536 g_return_val_if_fail (item != NULL, NULL);
538 return gst_webrtc_ice_stream_find_transport (item->stream, component);
542 /* TODO don't rely on libnice to (de)serialize candidates */
543 static NiceCandidateType
544 _candidate_type_from_string (const gchar * s)
546 if (g_strcmp0 (s, "host") == 0) {
547 return NICE_CANDIDATE_TYPE_HOST;
548 } else if (g_strcmp0 (s, "srflx") == 0) {
549 return NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
550 } else if (g_strcmp0 (s, "prflx") == 0) { /* FIXME: is the right string? */
551 return NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
552 } else if (g_strcmp0 (s, "relay") == 0) {
553 return NICE_CANDIDATE_TYPE_RELAY;
555 g_assert_not_reached ();
561 _candidate_type_to_string (NiceCandidateType type)
564 case NICE_CANDIDATE_TYPE_HOST:
566 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
568 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
570 case NICE_CANDIDATE_TYPE_RELAY:
573 g_assert_not_reached ();
578 static NiceCandidateTransport
579 _candidate_transport_from_string (const gchar * s)
581 if (g_strcmp0 (s, "UDP") == 0) {
582 return NICE_CANDIDATE_TRANSPORT_UDP;
583 } else if (g_strcmp0 (s, "TCP tcptype") == 0) {
584 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
585 } else if (g_strcmp0 (s, "tcp-passive") == 0) { /* FIXME: is the right string? */
586 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
587 } else if (g_strcmp0 (s, "tcp-so") == 0) {
588 return NICE_CANDIDATE_TRANSPORT_TCP_SO;
590 g_assert_not_reached ();
596 _candidate_type_to_string (NiceCandidateType type)
599 case NICE_CANDIDATE_TYPE_HOST:
601 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
603 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
605 case NICE_CANDIDATE_TYPE_RELAY:
608 g_assert_not_reached ();
614 /* parse the address for possible resolution */
616 get_candidate_address (const gchar * candidate, gchar ** prefix,
617 gchar ** address, gchar ** postfix)
619 char **tokens = NULL;
621 if (!g_str_has_prefix (candidate, "a=candidate:")) {
622 GST_ERROR ("candidate \"%s\" does not start with \"a=candidate:\"",
627 if (!(tokens = g_strsplit (candidate, " ", 6))) {
628 GST_ERROR ("candidate \"%s\" could not be tokenized", candidate);
632 if (g_strv_length (tokens) < 6) {
633 GST_ERROR ("candidate \"%s\" tokenization resulted in not enough tokens",
639 *address = g_strdup (tokens[4]);
642 *prefix = g_strjoinv (" ", tokens);
644 *postfix = g_strdup (tokens[5]);
655 /* must start with "a=candidate:" */
657 gst_webrtc_ice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
658 const gchar * candidate)
660 struct NiceStreamItem *item;
662 GSList *candidates = NULL;
664 item = _find_item (ice, -1, -1, stream);
665 g_return_if_fail (item != NULL);
668 nice_agent_parse_remote_candidate_sdp (ice->priv->nice_agent,
669 item->nice_stream_id, candidate);
671 /* might be a .local candidate */
672 char *prefix = NULL, *address = NULL, *postfix = NULL;
673 char *new_addr, *new_candidate;
674 char *new_candv[4] = { NULL, };
676 if (!get_candidate_address (candidate, &prefix, &address, &postfix)) {
677 GST_WARNING_OBJECT (ice, "Failed to retrieve address from candidate %s",
682 if (!g_str_has_suffix (address, ".local")) {
683 GST_WARNING_OBJECT (ice, "candidate address \'%s\' does not end "
684 "with \'.local\'", address);
689 if (!(new_addr = _resolve_host (ice, address))) {
690 GST_WARNING_OBJECT (ice, "Failed to resolve %s", address);
694 new_candv[0] = prefix;
695 new_candv[1] = new_addr;
696 new_candv[2] = postfix;
698 new_candidate = g_strjoinv (" ", new_candv);
700 GST_DEBUG_OBJECT (ice, "resolved to candidate %s", new_candidate);
703 nice_agent_parse_remote_candidate_sdp (ice->priv->nice_agent,
704 item->nice_stream_id, new_candidate);
705 g_free (new_candidate);
707 GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'",
725 candidates = g_slist_append (candidates, cand);
727 nice_agent_set_remote_candidates (ice->priv->nice_agent, item->nice_stream_id,
728 cand->component_id, candidates);
730 g_slist_free (candidates);
731 nice_candidate_free (cand);
735 gst_webrtc_ice_set_remote_credentials (GstWebRTCICE * ice,
736 GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
738 struct NiceStreamItem *item;
740 g_return_val_if_fail (ufrag != NULL, FALSE);
741 g_return_val_if_fail (pwd != NULL, FALSE);
742 item = _find_item (ice, -1, -1, stream);
743 g_return_val_if_fail (item != NULL, FALSE);
745 GST_DEBUG_OBJECT (ice, "Setting remote ICE credentials on "
746 "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
748 nice_agent_set_remote_credentials (ice->priv->nice_agent,
749 item->nice_stream_id, ufrag, pwd);
755 gst_webrtc_ice_add_turn_server (GstWebRTCICE * ice, const gchar * uri)
757 gboolean ret = FALSE;
760 if (!(valid_uri = _validate_turn_server (ice, uri)))
763 g_hash_table_insert (ice->turn_servers, g_strdup (uri), valid_uri);
772 gst_webrtc_ice_add_local_ip_address (GstWebRTCICE * ice, const gchar * address)
774 gboolean ret = FALSE;
775 NiceAddress nice_addr;
777 nice_address_init (&nice_addr);
779 ret = nice_address_set_from_string (&nice_addr, address);
782 ret = nice_agent_add_local_address (ice->priv->nice_agent, &nice_addr);
784 GST_ERROR_OBJECT (ice, "Failed to add local address to NiceAgent");
787 GST_ERROR_OBJECT (ice, "Failed to initialize NiceAddress [%s]", address);
794 gst_webrtc_ice_set_local_credentials (GstWebRTCICE * ice,
795 GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
797 struct NiceStreamItem *item;
799 g_return_val_if_fail (ufrag != NULL, FALSE);
800 g_return_val_if_fail (pwd != NULL, FALSE);
801 item = _find_item (ice, -1, -1, stream);
802 g_return_val_if_fail (item != NULL, FALSE);
804 GST_DEBUG_OBJECT (ice, "Setting local ICE credentials on "
805 "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
807 nice_agent_set_local_credentials (ice->priv->nice_agent, item->nice_stream_id,
814 gst_webrtc_ice_gather_candidates (GstWebRTCICE * ice,
815 GstWebRTCICEStream * stream)
817 struct NiceStreamItem *item;
819 item = _find_item (ice, -1, -1, stream);
820 g_return_val_if_fail (item != NULL, FALSE);
822 GST_DEBUG_OBJECT (ice, "gather candidates for stream %u",
823 item->nice_stream_id);
825 return gst_webrtc_ice_stream_gather_candidates (stream);
829 gst_webrtc_ice_set_is_controller (GstWebRTCICE * ice, gboolean controller)
831 g_object_set (G_OBJECT (ice->priv->nice_agent), "controlling-mode",
836 gst_webrtc_ice_get_is_controller (GstWebRTCICE * ice)
839 g_object_get (G_OBJECT (ice->priv->nice_agent), "controlling-mode",
845 gst_webrtc_ice_set_force_relay (GstWebRTCICE * ice, gboolean force_relay)
847 g_object_set (G_OBJECT (ice->priv->nice_agent), "force-relay", force_relay,
852 gst_webrtc_ice_set_on_ice_candidate (GstWebRTCICE * ice,
853 GstWebRTCIceOnCandidateFunc func, gpointer user_data, GDestroyNotify notify)
855 if (ice->priv->on_candidate_notify)
856 ice->priv->on_candidate_notify (ice->priv->on_candidate_data);
857 ice->priv->on_candidate = NULL;
859 ice->priv->on_candidate = func;
860 ice->priv->on_candidate_data = user_data;
861 ice->priv->on_candidate_notify = notify;
865 _clear_ice_stream (struct NiceStreamItem *item)
871 g_signal_handlers_disconnect_by_data (item->stream->ice->priv->nice_agent,
873 gst_object_unref (item->stream);
878 _validate_turn_server (GstWebRTCICE * ice, const gchar * s)
880 GstUri *uri = gst_uri_from_string_escaped (s);
881 const gchar *userinfo, *scheme;
882 GList *keys = NULL, *l;
883 gchar *user = NULL, *pass = NULL;
884 gboolean turn_tls = FALSE;
887 GST_DEBUG_OBJECT (ice, "validating turn server, %s", s);
890 GST_ERROR_OBJECT (ice, "Could not parse turn server '%s'", s);
894 scheme = gst_uri_get_scheme (uri);
895 if (g_strcmp0 (scheme, "turn") == 0) {
896 } else if (g_strcmp0 (scheme, "turns") == 0) {
899 GST_ERROR_OBJECT (ice, "unknown scheme '%s'", scheme);
903 keys = gst_uri_get_query_keys (uri);
904 for (l = keys; l; l = l->next) {
905 gchar *key = l->data;
907 if (g_strcmp0 (key, "transport") == 0) {
908 const gchar *transport = gst_uri_get_query_value (uri, "transport");
910 } else if (g_strcmp0 (transport, "udp") == 0) {
911 } else if (g_strcmp0 (transport, "tcp") == 0) {
913 GST_ERROR_OBJECT (ice, "unknown transport value, '%s'", transport);
917 GST_ERROR_OBJECT (ice, "unknown query key, '%s'", key);
922 /* TODO: Implement error checking similar to the stun server below */
923 userinfo = gst_uri_get_userinfo (uri);
924 _parse_userinfo (userinfo, &user, &pass);
926 GST_ERROR_OBJECT (ice, "No username specified in '%s'", s);
930 GST_ERROR_OBJECT (ice, "No password specified in '%s'", s);
934 port = gst_uri_get_port (uri);
936 if (port == GST_URI_NO_PORT) {
938 gst_uri_set_port (uri, 5349);
940 gst_uri_set_port (uri, 3478);
953 gst_webrtc_ice_set_stun_server (GstWebRTCICE * ice, const gchar * uri_s)
955 GstUri *uri = gst_uri_from_string_escaped (uri_s);
956 const gchar *msg = "must be of the form stun://<host>:<port>";
958 GST_DEBUG_OBJECT (ice, "setting stun server, %s", uri_s);
961 GST_ERROR_OBJECT (ice, "Couldn't parse stun server '%s', %s", uri_s, msg);
965 if (ice->stun_server)
966 gst_uri_unref (ice->stun_server);
967 ice->stun_server = uri;
971 gst_webrtc_ice_get_stun_server (GstWebRTCICE * ice)
973 if (ice->stun_server)
974 return gst_uri_to_string (ice->stun_server);
980 gst_webrtc_ice_set_turn_server (GstWebRTCICE * ice, const gchar * uri_s)
982 GstUri *uri = _validate_turn_server (ice, uri_s);
985 if (ice->turn_server)
986 gst_uri_unref (ice->turn_server);
987 ice->turn_server = uri;
992 gst_webrtc_ice_get_turn_server (GstWebRTCICE * ice)
994 if (ice->turn_server)
995 return gst_uri_to_string (ice->turn_server);
1001 gst_webrtc_ice_set_property (GObject * object, guint prop_id,
1002 const GValue * value, GParamSpec * pspec)
1004 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1008 g_object_set_property (G_OBJECT (ice->priv->nice_agent),
1012 g_object_set_property (G_OBJECT (ice->priv->nice_agent),
1016 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1022 gst_webrtc_ice_get_property (GObject * object, guint prop_id,
1023 GValue * value, GParamSpec * pspec)
1025 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1029 g_value_set_object (value, ice->priv->nice_agent);
1032 g_object_get_property (G_OBJECT (ice->priv->nice_agent),
1036 g_object_get_property (G_OBJECT (ice->priv->nice_agent),
1040 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1046 gst_webrtc_ice_finalize (GObject * object)
1048 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1050 g_signal_handlers_disconnect_by_data (ice->priv->nice_agent, ice);
1054 if (ice->priv->on_candidate_notify)
1055 ice->priv->on_candidate_notify (ice->priv->on_candidate_data);
1056 ice->priv->on_candidate = NULL;
1057 ice->priv->on_candidate_notify = NULL;
1059 if (ice->turn_server)
1060 gst_uri_unref (ice->turn_server);
1061 if (ice->stun_server)
1062 gst_uri_unref (ice->stun_server);
1064 g_mutex_clear (&ice->priv->lock);
1065 g_cond_clear (&ice->priv->cond);
1067 g_array_free (ice->priv->nice_stream_map, TRUE);
1069 g_object_unref (ice->priv->nice_agent);
1071 g_hash_table_unref (ice->turn_servers);
1073 G_OBJECT_CLASS (parent_class)->finalize (object);
1077 gst_webrtc_ice_constructed (GObject * object)
1079 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1081 _start_thread (ice);
1083 ice->priv->nice_agent = nice_agent_new (ice->priv->main_context,
1084 NICE_COMPATIBILITY_RFC5245);
1085 g_signal_connect (ice->priv->nice_agent, "new-candidate-full",
1086 G_CALLBACK (_on_new_candidate), ice);
1088 G_OBJECT_CLASS (parent_class)->constructed (object);
1092 gst_webrtc_ice_class_init (GstWebRTCICEClass * klass)
1094 GObjectClass *gobject_class = (GObjectClass *) klass;
1096 gobject_class->constructed = gst_webrtc_ice_constructed;
1097 gobject_class->get_property = gst_webrtc_ice_get_property;
1098 gobject_class->set_property = gst_webrtc_ice_set_property;
1099 gobject_class->finalize = gst_webrtc_ice_finalize;
1101 g_object_class_install_property (gobject_class,
1103 g_param_spec_object ("agent", "ICE agent",
1104 "ICE agent in use by this object. WARNING! Accessing this property "
1105 "may have disastrous consequences for the operation of webrtcbin. "
1106 "Other ICE implementations may not have the same interface.",
1107 NICE_TYPE_AGENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1109 g_object_class_install_property (gobject_class,
1111 g_param_spec_boolean ("ice-tcp", "ICE TCP",
1112 "Whether the agent should use ICE-TCP when gathering candidates",
1113 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1115 g_object_class_install_property (gobject_class,
1117 g_param_spec_boolean ("ice-udp", "ICE UDP",
1118 "Whether the agent should use ICE-UDP when gathering candidates",
1119 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1122 * GstWebRTCICE::add-local-ip-address:
1123 * @object: the #GstWebRTCICE
1124 * @address: The local IP address
1126 * Add a local IP address to use for ICE candidate gathering. If none
1127 * are supplied, they will be discovered automatically. Calling this signal
1128 * stops automatic ICE gathering.
1130 * Returns: whether the address could be added.
1132 gst_webrtc_ice_signals[ADD_LOCAL_IP_ADDRESS_SIGNAL] =
1133 g_signal_new_class_handler ("add-local-ip-address",
1134 G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1135 G_CALLBACK (gst_webrtc_ice_add_local_ip_address), NULL, NULL,
1136 g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
1140 gst_webrtc_ice_init (GstWebRTCICE * ice)
1142 ice->priv = gst_webrtc_ice_get_instance_private (ice);
1144 g_mutex_init (&ice->priv->lock);
1145 g_cond_init (&ice->priv->cond);
1148 g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1149 (GDestroyNotify) gst_uri_unref);
1151 ice->priv->nice_stream_map =
1152 g_array_new (FALSE, TRUE, sizeof (struct NiceStreamItem));
1153 g_array_set_clear_func (ice->priv->nice_stream_map,
1154 (GDestroyNotify) _clear_ice_stream);
1158 gst_webrtc_ice_new (const gchar * name)
1160 return g_object_new (GST_TYPE_WEBRTC_ICE, "name", name, NULL);