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.
25 #include "nicestream.h"
29 #define HTTP_PROXY_PORT_DEFAULT 3128
33 * - are locally generated remote candidates meant to be readded to libnice?
36 static GstUri *_validate_turn_server (GstWebRTCNice * ice, const gchar * s);
38 #define GST_CAT_DEFAULT gst_webrtc_nice_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
51 struct _GstWebRTCNicePrivate
53 NiceAgent *nice_agent;
55 GArray *nice_stream_map;
58 GMainContext *main_context;
63 GstWebRTCICEOnCandidateFunc on_candidate;
64 gpointer on_candidate_data;
65 GDestroyNotify on_candidate_notify;
70 GHashTable *turn_servers;
75 #define gst_webrtc_nice_parent_class parent_class
76 G_DEFINE_TYPE_WITH_CODE (GstWebRTCNice, gst_webrtc_nice,
77 GST_TYPE_WEBRTC_ICE, G_ADD_PRIVATE (GstWebRTCNice)
78 GST_DEBUG_CATEGORY_INIT (gst_webrtc_nice_debug, "webrtcnice", 0,
82 _unlock_pc_thread (GMutex * lock)
84 g_mutex_unlock (lock);
85 return G_SOURCE_REMOVE;
89 _gst_nice_thread (GstWebRTCNice * ice)
91 g_mutex_lock (&ice->priv->lock);
92 ice->priv->main_context = g_main_context_new ();
93 ice->priv->loop = g_main_loop_new (ice->priv->main_context, FALSE);
95 g_cond_broadcast (&ice->priv->cond);
96 g_main_context_invoke (ice->priv->main_context,
97 (GSourceFunc) _unlock_pc_thread, &ice->priv->lock);
99 g_main_loop_run (ice->priv->loop);
101 g_mutex_lock (&ice->priv->lock);
102 g_main_context_unref (ice->priv->main_context);
103 ice->priv->main_context = NULL;
104 g_main_loop_unref (ice->priv->loop);
105 ice->priv->loop = NULL;
106 g_cond_broadcast (&ice->priv->cond);
107 g_mutex_unlock (&ice->priv->lock);
113 _start_thread (GstWebRTCNice * ice)
115 g_mutex_lock (&ice->priv->lock);
116 ice->priv->thread = g_thread_new (GST_OBJECT_NAME (ice),
117 (GThreadFunc) _gst_nice_thread, ice);
119 while (!ice->priv->loop)
120 g_cond_wait (&ice->priv->cond, &ice->priv->lock);
121 g_mutex_unlock (&ice->priv->lock);
125 _stop_thread (GstWebRTCNice * ice)
127 g_mutex_lock (&ice->priv->lock);
128 g_main_loop_quit (ice->priv->loop);
129 while (ice->priv->loop)
130 g_cond_wait (&ice->priv->cond, &ice->priv->lock);
131 g_mutex_unlock (&ice->priv->lock);
133 g_thread_unref (ice->priv->thread);
136 struct NiceStreamItem
139 guint nice_stream_id;
140 GstWebRTCICEStream *stream;
143 /* TRUE to continue, FALSE to stop */
144 typedef gboolean (*NiceStreamItemForeachFunc) (struct NiceStreamItem * item,
148 _nice_stream_item_foreach (GstWebRTCNice * ice, NiceStreamItemForeachFunc func,
153 len = ice->priv->nice_stream_map->len;
154 for (i = 0; i < len; i++) {
155 struct NiceStreamItem *item =
156 &g_array_index (ice->priv->nice_stream_map, struct NiceStreamItem,
159 if (!func (item, data))
164 /* TRUE for match, FALSE otherwise */
165 typedef gboolean (*NiceStreamItemFindFunc) (struct NiceStreamItem * item,
170 NiceStreamItemFindFunc func;
172 struct NiceStreamItem *ret;
176 _find_nice_item (struct NiceStreamItem *item, gpointer user_data)
178 struct nice_find *f = user_data;
179 if (f->func (item, f->data)) {
186 static struct NiceStreamItem *
187 _nice_stream_item_find (GstWebRTCNice * ice, NiceStreamItemFindFunc func,
196 _nice_stream_item_foreach (ice, _find_nice_item, &f);
201 #define NICE_MATCH_INIT { -1, -1, NULL }
204 _match (struct NiceStreamItem *item, struct NiceStreamItem *m)
206 if (m->session_id != -1 && m->session_id != item->session_id)
208 if (m->nice_stream_id != -1 && m->nice_stream_id != item->nice_stream_id)
210 if (m->stream != NULL && m->stream != item->stream)
216 static struct NiceStreamItem *
217 _find_item (GstWebRTCNice * ice, guint session_id, guint nice_stream_id,
218 GstWebRTCICEStream * stream)
220 struct NiceStreamItem m = NICE_MATCH_INIT;
222 m.session_id = session_id;
223 m.nice_stream_id = nice_stream_id;
226 return _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
229 static struct NiceStreamItem *
230 _create_nice_stream_item (GstWebRTCNice * ice, guint session_id)
232 struct NiceStreamItem item;
234 item.session_id = session_id;
235 item.nice_stream_id = nice_agent_add_stream (ice->priv->nice_agent, 1);
237 GST_WEBRTC_ICE_STREAM (gst_webrtc_nice_stream_new (GST_WEBRTC_ICE (ice),
241 g_array_append_val (ice->priv->nice_stream_map, item);
243 return _find_item (ice, item.session_id, item.nice_stream_id, item.stream);
247 _parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass)
257 colon = g_strstr_len (userinfo, -1, ":");
259 *user = g_uri_unescape_string (userinfo, NULL);
264 /* Check that the first occurence is also the last occurence */
265 if (colon != g_strrstr (userinfo, ":"))
266 GST_WARNING ("userinfo %s contains more than one ':', will assume that the "
267 "first ':' delineates user:pass. You should escape the user and pass "
268 "before adding to the URI.", userinfo);
270 *user = g_uri_unescape_segment (userinfo, colon, NULL);
271 *pass = g_uri_unescape_string (&colon[1], NULL);
274 struct resolve_host_data
278 gboolean main_context_handled;
280 GDestroyNotify notify;
284 on_resolve_host (GResolver * resolver, GAsyncResult * res, gpointer user_data)
286 GTask *task = user_data;
287 struct resolve_host_data *rh;
288 GError *error = NULL;
291 rh = g_task_get_task_data (task);
293 if (!(addresses = g_resolver_lookup_by_name_finish (resolver, res, &error))) {
294 GST_ERROR ("failed to resolve: %s", error->message);
295 g_task_return_error (task, error);
296 g_object_unref (task);
300 GST_DEBUG_OBJECT (rh->ice, "Resolved %d addresses for host %s with data %p",
301 g_list_length (addresses), rh->host, rh);
303 g_task_return_pointer (task, addresses,
304 (GDestroyNotify) g_resolver_free_addresses);
305 g_object_unref (task);
309 free_resolve_host_data (struct resolve_host_data *rh)
311 GST_TRACE_OBJECT (rh->ice, "Freeing data %p for resolving host %s", rh,
315 rh->notify (rh->user_data);
321 static struct resolve_host_data *
322 resolve_host_data_new (GstWebRTCNice * ice, const char *host)
324 struct resolve_host_data *rh = g_new0 (struct resolve_host_data, 1);
327 rh->host = g_strdup (host);
333 resolve_host_main_cb (gpointer user_data)
335 GResolver *resolver = g_resolver_get_default ();
336 GTask *task = user_data;
337 struct resolve_host_data *rh;
339 rh = g_task_get_task_data (task);
340 /* no need to error anymore if the main context disappears and this task is
342 rh->main_context_handled = TRUE;
344 GST_DEBUG_OBJECT (rh->ice, "Resolving host %s", rh->host);
345 g_resolver_lookup_by_name_async (resolver, rh->host, NULL,
346 (GAsyncReadyCallback) on_resolve_host, g_object_ref (task));
348 return G_SOURCE_REMOVE;
352 error_task_if_unhandled (GTask * task)
354 struct resolve_host_data *rh;
356 rh = g_task_get_task_data (task);
358 if (!rh->main_context_handled) {
359 GST_DEBUG_OBJECT (rh->ice, "host resolve for %s with data %p was never "
360 "executed, main context quit?", rh->host, rh);
361 g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "%s",
365 g_object_unref (task);
369 resolve_host_async (GstWebRTCNice * ice, const gchar * host,
370 GAsyncReadyCallback cb, gpointer user_data, GDestroyNotify notify)
372 struct resolve_host_data *rh = resolve_host_data_new (ice, host);
375 rh->user_data = user_data;
377 task = g_task_new (rh->ice, NULL, cb, user_data);
379 g_task_set_task_data (task, rh, (GDestroyNotify) free_resolve_host_data);
381 GST_TRACE_OBJECT (rh->ice, "invoking main context for resolving host %s "
382 "with data %p", host, rh);
383 g_main_context_invoke_full (ice->priv->main_context, G_PRIORITY_DEFAULT,
384 resolve_host_main_cb, task, (GDestroyNotify) error_task_if_unhandled);
388 resolve_host_finish (GstWebRTCNice * ice, GAsyncResult * res, GError ** error)
390 g_return_val_if_fail (g_task_is_valid (res, ice), NULL);
392 return g_task_propagate_pointer (G_TASK (res), error);
396 _add_turn_server (GstWebRTCNice * ice, struct NiceStreamItem *item,
397 GstUri * turn_server)
399 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
401 NiceRelayType relays[4] = { 0, };
403 const gchar *userinfo, *transport, *scheme;
406 host = gst_uri_get_host (turn_server);
408 GST_ERROR_OBJECT (ice, "Turn server has no host");
412 scheme = gst_uri_get_scheme (turn_server);
413 transport = gst_uri_get_query_value (turn_server, "transport");
414 userinfo = gst_uri_get_userinfo (turn_server);
415 _parse_userinfo (userinfo, &user, &pass);
417 if (g_strcmp0 (scheme, "turns") == 0) {
418 relays[relay_n++] = NICE_RELAY_TYPE_TURN_TLS;
419 } else if (g_strcmp0 (scheme, "turn") == 0) {
420 if (!transport || g_strcmp0 (transport, "udp") == 0)
421 relays[relay_n++] = NICE_RELAY_TYPE_TURN_UDP;
422 if (!transport || g_strcmp0 (transport, "tcp") == 0)
423 relays[relay_n++] = NICE_RELAY_TYPE_TURN_TCP;
425 g_assert (relay_n < G_N_ELEMENTS (relays));
427 for (i = 0; i < relay_n; i++) {
428 if (!nice_agent_set_relay_info (nice->priv->nice_agent,
429 item->nice_stream_id, NICE_COMPONENT_TYPE_RTP,
430 gst_uri_get_host (turn_server), gst_uri_get_port (turn_server),
431 user, pass, relays[i])) {
432 gchar *uri_str = gst_uri_to_string (turn_server);
433 GST_ERROR_OBJECT (ice, "Could not set TURN server %s on libnice",
447 struct NiceStreamItem *item;
451 _add_turn_server_func (const gchar * uri, GstUri * turn_server,
452 AddTurnServerData * data)
454 _add_turn_server (data->ice, data->item, turn_server);
458 _add_stun_server (GstWebRTCNice * ice, GstUri * stun_server)
460 const gchar *msg = "must be of the form stun://<host>:<port>";
465 s = gst_uri_to_string (stun_server);
466 GST_DEBUG_OBJECT (ice, "adding stun server, %s", s);
468 host = gst_uri_get_host (stun_server);
470 GST_ERROR_OBJECT (ice, "Stun server '%s' has no host, %s", s, msg);
474 port = gst_uri_get_port (stun_server);
475 if (port == GST_URI_NO_PORT) {
476 GST_INFO_OBJECT (ice, "Stun server '%s' has no port, assuming 3478", s);
478 gst_uri_set_port (stun_server, port);
481 g_object_set (ice->priv->nice_agent, "stun-server", host,
482 "stun-server-port", port, NULL);
488 static GstWebRTCICEStream *
489 gst_webrtc_nice_add_stream (GstWebRTCICE * ice, guint session_id)
491 struct NiceStreamItem m = NICE_MATCH_INIT;
492 struct NiceStreamItem *item;
493 AddTurnServerData add_data;
494 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
496 m.session_id = session_id;
497 item = _nice_stream_item_find (nice, (NiceStreamItemFindFunc) _match, &m);
499 GST_ERROR_OBJECT (nice, "stream already added with session_id=%u",
504 if (nice->priv->stun_server) {
505 _add_stun_server (nice, nice->priv->stun_server);
508 item = _create_nice_stream_item (nice, session_id);
510 if (nice->priv->turn_server) {
511 _add_turn_server (nice, item, nice->priv->turn_server);
515 add_data.item = item;
517 g_hash_table_foreach (nice->priv->turn_servers,
518 (GHFunc) _add_turn_server_func, &add_data);
524 _on_new_candidate (NiceAgent * agent, NiceCandidate * candidate,
527 struct NiceStreamItem *item;
530 item = _find_item (ice, -1, candidate->stream_id, NULL);
532 GST_WARNING_OBJECT (ice, "received signal for non-existent stream %u",
533 candidate->stream_id);
537 if (!candidate->username || !candidate->password) {
538 gboolean got_credentials;
539 gchar *ufrag, *password;
541 got_credentials = nice_agent_get_local_credentials (ice->priv->nice_agent,
542 candidate->stream_id, &ufrag, &password);
543 g_warn_if_fail (got_credentials);
545 if (!candidate->username)
546 candidate->username = ufrag;
550 if (!candidate->password)
551 candidate->password = password;
556 attr = nice_agent_generate_local_candidate_sdp (agent, candidate);
558 if (ice->priv->on_candidate)
559 ice->priv->on_candidate (GST_WEBRTC_ICE (ice), item->session_id, attr,
560 ice->priv->on_candidate_data);
565 static GstWebRTCICETransport *
566 gst_webrtc_nice_find_transport (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
567 GstWebRTCICEComponent component)
569 struct NiceStreamItem *item;
570 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
572 item = _find_item (nice, -1, -1, stream);
573 g_return_val_if_fail (item != NULL, NULL);
575 return gst_webrtc_ice_stream_find_transport (item->stream, component);
579 /* TODO don't rely on libnice to (de)serialize candidates */
580 static NiceCandidateType
581 _candidate_type_from_string (const gchar * s)
583 if (g_strcmp0 (s, "host") == 0) {
584 return NICE_CANDIDATE_TYPE_HOST;
585 } else if (g_strcmp0 (s, "srflx") == 0) {
586 return NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
587 } else if (g_strcmp0 (s, "prflx") == 0) { /* FIXME: is the right string? */
588 return NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
589 } else if (g_strcmp0 (s, "relay") == 0) {
590 return NICE_CANDIDATE_TYPE_RELAY;
592 g_assert_not_reached ();
598 _candidate_type_to_string (NiceCandidateType type)
601 case NICE_CANDIDATE_TYPE_HOST:
603 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
605 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
607 case NICE_CANDIDATE_TYPE_RELAY:
610 g_assert_not_reached ();
615 static NiceCandidateTransport
616 _candidate_transport_from_string (const gchar * s)
618 if (g_strcmp0 (s, "UDP") == 0) {
619 return NICE_CANDIDATE_TRANSPORT_UDP;
620 } else if (g_strcmp0 (s, "TCP tcptype") == 0) {
621 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
622 } else if (g_strcmp0 (s, "tcp-passive") == 0) { /* FIXME: is the right string? */
623 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
624 } else if (g_strcmp0 (s, "tcp-so") == 0) {
625 return NICE_CANDIDATE_TRANSPORT_TCP_SO;
627 g_assert_not_reached ();
633 _candidate_type_to_string (NiceCandidateType type)
636 case NICE_CANDIDATE_TYPE_HOST:
638 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
640 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
642 case NICE_CANDIDATE_TYPE_RELAY:
645 g_assert_not_reached ();
651 /* parse the address for possible resolution */
653 get_candidate_address (const gchar * candidate, gchar ** prefix,
654 gchar ** address, gchar ** postfix)
656 char **tokens = NULL;
657 char *tmp_address = NULL;
659 if (!g_str_has_prefix (candidate, "a=candidate:")) {
660 GST_ERROR ("candidate \"%s\" does not start with \"a=candidate:\"",
665 if (!(tokens = g_strsplit (candidate, " ", 6))) {
666 GST_ERROR ("candidate \"%s\" could not be tokenized", candidate);
670 if (g_strv_length (tokens) < 6) {
671 GST_ERROR ("candidate \"%s\" tokenization resulted in not enough tokens",
676 tmp_address = tokens[4];
678 *address = g_strdup (tmp_address);
682 *prefix = g_strjoinv (" ", tokens);
684 *postfix = g_strdup (tokens[5]);
686 tokens[4] = tmp_address;
697 struct resolve_candidate_data
699 guint nice_stream_id;
705 free_resolve_candidate_data (struct resolve_candidate_data *rc)
708 g_free (rc->postfix);
713 add_ice_candidate_to_libnice (GstWebRTCICE * ice, guint nice_stream_id,
714 NiceCandidate * cand)
716 GSList *candidates = NULL;
717 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
719 if (cand->component_id == 2) {
720 /* we only support rtcp-mux so rtcp candidates are useless for us */
721 GST_INFO_OBJECT (ice, "Dropping RTCP candidate");
725 candidates = g_slist_append (candidates, cand);
727 nice_agent_set_remote_candidates (nice->priv->nice_agent, nice_stream_id,
728 cand->component_id, candidates);
730 g_slist_free (candidates);
734 on_candidate_resolved (GstWebRTCICE * ice, GAsyncResult * res,
737 struct resolve_candidate_data *rc = user_data;
738 GError *error = NULL;
740 char *new_candv[4] = { NULL, };
741 char *new_addr, *new_candidate;
743 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
745 if (!(addresses = resolve_host_finish (nice, res, &error))) {
746 GST_WARNING_OBJECT (ice, "Could not resolve candidate address: %s",
748 g_clear_error (&error);
752 new_addr = g_inet_address_to_string (addresses->data);
753 g_resolver_free_addresses (addresses);
756 new_candv[0] = rc->prefix;
757 new_candv[1] = new_addr;
758 new_candv[2] = rc->postfix;
760 new_candidate = g_strjoinv (" ", new_candv);
762 GST_DEBUG_OBJECT (ice, "resolved to candidate %s", new_candidate);
765 nice_agent_parse_remote_candidate_sdp (nice->priv->nice_agent,
766 rc->nice_stream_id, new_candidate);
767 g_free (new_candidate);
769 GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'", new_candidate);
775 add_ice_candidate_to_libnice (ice, rc->nice_stream_id, cand);
776 nice_candidate_free (cand);
779 /* candidate must start with "a=candidate:" or be NULL*/
781 gst_webrtc_nice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
782 const gchar * candidate)
784 struct NiceStreamItem *item;
786 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
788 item = _find_item (nice, -1, -1, stream);
789 g_return_if_fail (item != NULL);
791 if (candidate == NULL) {
792 nice_agent_peer_candidate_gathering_done (nice->priv->nice_agent,
793 item->nice_stream_id);
798 nice_agent_parse_remote_candidate_sdp (nice->priv->nice_agent,
799 item->nice_stream_id, candidate);
801 /* might be a .local candidate */
802 char *prefix = NULL, *address = NULL, *postfix = NULL;
803 struct resolve_candidate_data *rc;
805 if (!get_candidate_address (candidate, &prefix, &address, &postfix)) {
806 GST_WARNING_OBJECT (nice, "Failed to retrieve address from candidate %s",
811 if (!g_str_has_suffix (address, ".local")) {
812 GST_WARNING_OBJECT (nice, "candidate address \'%s\' does not end "
813 "with \'.local\'", address);
817 rc = g_new0 (struct resolve_candidate_data, 1);
818 rc->nice_stream_id = item->nice_stream_id;
820 rc->postfix = postfix;
821 resolve_host_async (nice, address,
822 (GAsyncReadyCallback) on_candidate_resolved, rc,
823 (GDestroyNotify) free_resolve_candidate_data);
829 g_clear_pointer (&address, g_free);
830 g_clear_pointer (&prefix, g_free);
831 g_clear_pointer (&postfix, g_free);
836 add_ice_candidate_to_libnice (ice, item->nice_stream_id, cand);
837 nice_candidate_free (cand);
841 gst_webrtc_nice_set_remote_credentials (GstWebRTCICE * ice,
842 GstWebRTCICEStream * stream, const gchar * ufrag, const gchar * pwd)
844 struct NiceStreamItem *item;
845 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
847 g_return_val_if_fail (ufrag != NULL, FALSE);
848 g_return_val_if_fail (pwd != NULL, FALSE);
849 item = _find_item (nice, -1, -1, stream);
850 g_return_val_if_fail (item != NULL, FALSE);
852 GST_DEBUG_OBJECT (nice, "Setting remote ICE credentials on "
853 "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
855 nice_agent_set_remote_credentials (nice->priv->nice_agent,
856 item->nice_stream_id, ufrag, pwd);
865 } AddTurnServerToStreamData;
868 _add_turn_server_foreach_stream_func (struct NiceStreamItem *item,
871 AddTurnServerToStreamData *add_data = (AddTurnServerToStreamData *) data;
872 _add_turn_server (add_data->ice, item, add_data->turn_server);
877 gst_webrtc_nice_add_turn_server (GstWebRTCICE * ice, const gchar * uri)
879 gboolean ret = FALSE;
881 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
883 AddTurnServerToStreamData add_data;
885 if (!(valid_uri = _validate_turn_server (nice, uri)))
889 g_hash_table_insert (nice->priv->turn_servers, g_strdup (uri), valid_uri);
891 /* add the turn server to any streams that were already created */
894 add_data.turn_server = valid_uri;
895 _nice_stream_item_foreach (nice, _add_turn_server_foreach_stream_func,
906 gst_webrtc_nice_add_local_ip_address (GstWebRTCNice * ice,
907 const gchar * address)
909 gboolean ret = FALSE;
910 NiceAddress nice_addr;
912 nice_address_init (&nice_addr);
914 ret = nice_address_set_from_string (&nice_addr, address);
917 ret = nice_agent_add_local_address (ice->priv->nice_agent, &nice_addr);
919 GST_ERROR_OBJECT (ice, "Failed to add local address to NiceAgent");
922 GST_ERROR_OBJECT (ice, "Failed to initialize NiceAddress [%s]", address);
929 gst_webrtc_nice_set_local_credentials (GstWebRTCICE * ice,
930 GstWebRTCICEStream * stream, const gchar * ufrag, const gchar * pwd)
932 struct NiceStreamItem *item;
933 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
935 g_return_val_if_fail (ufrag != NULL, FALSE);
936 g_return_val_if_fail (pwd != NULL, FALSE);
937 item = _find_item (nice, -1, -1, stream);
938 g_return_val_if_fail (item != NULL, FALSE);
940 GST_DEBUG_OBJECT (nice, "Setting local ICE credentials on "
941 "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
943 nice_agent_set_local_credentials (nice->priv->nice_agent,
944 item->nice_stream_id, ufrag, pwd);
950 gst_webrtc_nice_gather_candidates (GstWebRTCICE * ice,
951 GstWebRTCICEStream * stream)
953 struct NiceStreamItem *item;
954 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
956 item = _find_item (nice, -1, -1, stream);
957 g_return_val_if_fail (item != NULL, FALSE);
959 GST_DEBUG_OBJECT (nice, "gather candidates for stream %u",
960 item->nice_stream_id);
962 return gst_webrtc_ice_stream_gather_candidates (stream);
966 gst_webrtc_nice_set_is_controller (GstWebRTCICE * ice, gboolean controller)
968 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
969 g_object_set (G_OBJECT (nice->priv->nice_agent), "controlling-mode",
974 gst_webrtc_nice_get_is_controller (GstWebRTCICE * ice)
977 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
978 g_object_get (G_OBJECT (nice->priv->nice_agent), "controlling-mode",
984 gst_webrtc_nice_set_force_relay (GstWebRTCICE * ice, gboolean force_relay)
986 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
987 g_object_set (G_OBJECT (nice->priv->nice_agent), "force-relay", force_relay,
992 gst_webrtc_nice_set_on_ice_candidate (GstWebRTCICE * ice,
993 GstWebRTCICEOnCandidateFunc func, gpointer user_data, GDestroyNotify notify)
995 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
996 if (nice->priv->on_candidate_notify)
997 nice->priv->on_candidate_notify (nice->priv->on_candidate_data);
998 nice->priv->on_candidate = NULL;
1000 nice->priv->on_candidate = func;
1001 nice->priv->on_candidate_data = user_data;
1002 nice->priv->on_candidate_notify = notify;
1006 gst_webrtc_nice_set_tos (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
1009 struct NiceStreamItem *item;
1010 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1012 item = _find_item (nice, -1, -1, stream);
1013 g_return_if_fail (item != NULL);
1015 nice_agent_set_stream_tos (nice->priv->nice_agent, item->nice_stream_id, tos);
1018 static const gchar *
1019 _relay_type_to_string (GstUri * turn_server)
1021 const gchar *scheme;
1022 const gchar *transport;
1027 scheme = gst_uri_get_scheme (turn_server);
1028 transport = gst_uri_get_query_value (turn_server, "transport");
1030 if (g_strcmp0 (scheme, "turns") == 0) {
1032 } else if (g_strcmp0 (scheme, "turn") == 0) {
1033 if (!transport || g_strcmp0 (transport, "udp") == 0)
1035 if (!transport || g_strcmp0 (transport, "tcp") == 0)
1043 _get_server_url (GstWebRTCNice * ice, NiceCandidate * cand)
1045 switch (cand->type) {
1046 case NICE_CANDIDATE_TYPE_RELAYED:{
1048 gchar ipaddr[NICE_ADDRESS_STRING_LEN];
1049 nice_candidate_relay_address (cand, &addr);
1050 nice_address_to_string (&addr, ipaddr);
1051 return g_strdup (ipaddr);
1053 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:{
1055 gchar ipaddr[NICE_ADDRESS_STRING_LEN];
1056 if (nice_candidate_stun_server_address (cand, &addr)) {
1057 nice_address_to_string (&addr, ipaddr);
1058 return g_strdup (ipaddr);
1060 return g_strdup (gst_uri_get_host (ice->priv->stun_server));
1062 return g_strdup (gst_uri_get_host (ice->priv->stun_server));
1065 return g_strdup ("");
1069 /* TODO: replace it with nice_candidate_type_to_string()
1070 * when it's ready for use
1071 * https://libnice.freedesktop.org/libnice/NiceCandidate.html#nice-candidate-type-to-string
1073 static const gchar *
1074 _candidate_type_to_string (NiceCandidateType type)
1077 case NICE_CANDIDATE_TYPE_HOST:
1079 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
1081 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
1083 case NICE_CANDIDATE_TYPE_RELAYED:
1086 g_assert_not_reached ();
1092 _populate_candidate_stats (GstWebRTCNice * ice, NiceCandidate * cand,
1093 GstWebRTCICEStream * stream, GstWebRTCICECandidateStats * stats,
1096 gchar ipaddr[INET6_ADDRSTRLEN];
1098 g_assert (cand != NULL);
1100 nice_address_to_string (&cand->addr, ipaddr);
1101 stats->port = nice_address_get_port (&cand->addr);
1102 stats->ipaddr = g_strdup (ipaddr);
1103 stats->stream_id = stream->stream_id;
1104 stats->type = _candidate_type_to_string (cand->type);
1105 stats->prio = cand->priority;
1107 cand->transport == NICE_CANDIDATE_TRANSPORT_UDP ? "udp" : "tcp";
1109 if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
1110 stats->relay_proto = _relay_type_to_string (ice->priv->turn_server);
1111 stats->url = _get_server_url (ice, cand);
1116 _populate_candidate_list_stats (GstWebRTCNice * ice, GSList * cands,
1117 GstWebRTCICEStream * stream, GPtrArray * result, gboolean is_local)
1121 for (item = cands; item != NULL; item = item->next) {
1122 GstWebRTCICECandidateStats *stats =
1123 g_malloc0 (sizeof (GstWebRTCICECandidateStats));
1124 NiceCandidate *c = item->data;
1125 _populate_candidate_stats (ice, c, stream, stats, is_local);
1126 g_ptr_array_add (result, stats);
1129 g_ptr_array_add (result, NULL);
1132 static GstWebRTCICECandidateStats **
1133 gst_webrtc_nice_get_local_candidates (GstWebRTCICE * ice,
1134 GstWebRTCICEStream * stream)
1136 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1137 GSList *cands = NULL;
1139 /* TODO: Use a g_ptr_array_new_null_terminated once when we depend on GLib 2.74 */
1140 GPtrArray *result = g_ptr_array_new ();
1142 cands = nice_agent_get_local_candidates (nice->priv->nice_agent,
1143 stream->stream_id, NICE_COMPONENT_TYPE_RTP);
1145 _populate_candidate_list_stats (nice, cands, stream, result, TRUE);
1146 g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
1148 return (GstWebRTCICECandidateStats **) g_ptr_array_free (result, FALSE);
1151 static GstWebRTCICECandidateStats **
1152 gst_webrtc_nice_get_remote_candidates (GstWebRTCICE * ice,
1153 GstWebRTCICEStream * stream)
1155 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1156 GSList *cands = NULL;
1158 /* TODO: Use a g_ptr_array_new_null_terminated once when we depend on GLib 2.74 */
1159 GPtrArray *result = g_ptr_array_new ();
1161 cands = nice_agent_get_remote_candidates (nice->priv->nice_agent,
1162 stream->stream_id, NICE_COMPONENT_TYPE_RTP);
1164 _populate_candidate_list_stats (nice, cands, stream, result, FALSE);
1165 g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
1167 return (GstWebRTCICECandidateStats **) g_ptr_array_free (result, FALSE);
1171 gst_webrtc_nice_get_selected_pair (GstWebRTCICE * ice,
1172 GstWebRTCICEStream * stream, GstWebRTCICECandidateStats ** local_stats,
1173 GstWebRTCICECandidateStats ** remote_stats)
1175 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1176 NiceCandidate *local_cand = NULL;
1177 NiceCandidate *remote_cand = NULL;
1181 if (nice_agent_get_selected_pair (nice->priv->nice_agent, stream->stream_id,
1182 NICE_COMPONENT_TYPE_RTP, &local_cand, &remote_cand)) {
1183 *local_stats = g_new0 (GstWebRTCICECandidateStats, 1);
1184 _populate_candidate_stats (nice, local_cand, stream, *local_stats, TRUE);
1186 *remote_stats = g_new0 (GstWebRTCICECandidateStats, 1);
1187 _populate_candidate_stats (nice, remote_cand, stream, *remote_stats,
1198 _clear_ice_stream (struct NiceStreamItem *item)
1200 GstWebRTCNice *ice = NULL;
1206 g_object_get (item->stream, "ice", &ice, NULL);
1209 g_signal_handlers_disconnect_by_data (ice->priv->nice_agent,
1211 gst_object_unref (ice);
1213 gst_object_unref (item->stream);
1218 _validate_turn_server (GstWebRTCNice * ice, const gchar * s)
1220 GstUri *uri = gst_uri_from_string_escaped (s);
1221 const gchar *userinfo, *scheme;
1222 GList *keys = NULL, *l;
1223 gchar *user = NULL, *pass = NULL;
1224 gboolean turn_tls = FALSE;
1227 GST_DEBUG_OBJECT (ice, "validating turn server, %s", s);
1230 GST_ERROR_OBJECT (ice, "Could not parse turn server '%s'", s);
1234 scheme = gst_uri_get_scheme (uri);
1235 if (g_strcmp0 (scheme, "turn") == 0) {
1236 } else if (g_strcmp0 (scheme, "turns") == 0) {
1239 GST_ERROR_OBJECT (ice, "unknown scheme '%s'", scheme);
1243 keys = gst_uri_get_query_keys (uri);
1244 for (l = keys; l; l = l->next) {
1245 gchar *key = l->data;
1247 if (g_strcmp0 (key, "transport") == 0) {
1248 const gchar *transport = gst_uri_get_query_value (uri, "transport");
1250 } else if (g_strcmp0 (transport, "udp") == 0) {
1251 } else if (g_strcmp0 (transport, "tcp") == 0) {
1253 GST_ERROR_OBJECT (ice, "unknown transport value, '%s'", transport);
1257 GST_ERROR_OBJECT (ice, "unknown query key, '%s'", key);
1262 /* TODO: Implement error checking similar to the stun server below */
1263 userinfo = gst_uri_get_userinfo (uri);
1264 _parse_userinfo (userinfo, &user, &pass);
1266 GST_ERROR_OBJECT (ice, "No username specified in '%s'", s);
1270 GST_ERROR_OBJECT (ice, "No password specified in '%s'", s);
1274 port = gst_uri_get_port (uri);
1276 if (port == GST_URI_NO_PORT) {
1278 gst_uri_set_port (uri, 5349);
1280 gst_uri_set_port (uri, 3478);
1294 gst_uri_unref (uri);
1300 on_http_proxy_resolved (GstWebRTCICE * ice, GAsyncResult * res,
1303 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1304 GstUri *uri = user_data;
1306 GError *error = NULL;
1307 const gchar *userinfo;
1311 guint port = GST_URI_NO_PORT;
1313 if (!(addresses = resolve_host_finish (nice, res, &error))) {
1314 GST_WARNING_OBJECT (ice, "Failed to resolve http proxy: %s",
1316 g_clear_error (&error);
1320 /* XXX: only the first IP is used */
1321 ip = g_inet_address_to_string (addresses->data);
1322 g_resolver_free_addresses (addresses);
1326 GST_ERROR_OBJECT (ice, "failed to resolve host for proxy");
1327 gst_uri_unref (uri);
1331 port = gst_uri_get_port (uri);
1332 if (port == GST_URI_NO_PORT) {
1333 port = HTTP_PROXY_PORT_DEFAULT;
1334 GST_DEBUG_OBJECT (ice, "Proxy server has no port, assuming %u",
1335 HTTP_PROXY_PORT_DEFAULT);
1338 userinfo = gst_uri_get_userinfo (uri);
1339 _parse_userinfo (userinfo, &user, &pass);
1341 g_object_set (nice->priv->nice_agent,
1342 "proxy-ip", ip, "proxy-port", port, "proxy-type", NICE_PROXY_TYPE_HTTP,
1343 "proxy-username", user, "proxy-password", pass, NULL);
1351 _set_http_proxy (GstWebRTCICE * ice, const gchar * s)
1353 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1354 GstUri *uri = gst_uri_from_string_escaped (s);
1356 "must be of the form http://[username:password@]<host>[:<port>]";
1357 const gchar *host = NULL;
1358 const gchar *userinfo;
1359 gchar *user = NULL, *pass = NULL;
1361 GST_DEBUG_OBJECT (ice, "setting http proxy %s", s);
1364 GST_ERROR_OBJECT (ice, "Couldn't parse http proxy uri '%s', %s", s, msg);
1368 if (g_strcmp0 (gst_uri_get_scheme (uri), "http") != 0) {
1369 GST_ERROR_OBJECT (ice,
1370 "Couldn't parse uri scheme for http proxy server '%s', %s", s, msg);
1371 gst_uri_unref (uri);
1375 host = gst_uri_get_host (uri);
1377 GST_ERROR_OBJECT (ice, "http proxy server '%s' has no host, %s", s, msg);
1378 gst_uri_unref (uri);
1382 userinfo = gst_uri_get_userinfo (uri);
1383 _parse_userinfo (userinfo, &user, &pass);
1384 if ((pass && pass[0] != '\0') && (!user || user[0] == '\0')) {
1385 GST_ERROR_OBJECT (ice,
1386 "Password specified without user for http proxy '%s', %s", s, msg);
1391 resolve_host_async (nice, host, (GAsyncReadyCallback) on_http_proxy_resolved,
1392 gst_uri_ref (uri), (GDestroyNotify) gst_uri_unref);
1402 gst_webrtc_nice_set_stun_server (GstWebRTCICE * ice, const gchar * uri_s)
1404 GstUri *uri = gst_uri_from_string_escaped (uri_s);
1405 const gchar *msg = "must be of the form stun://<host>:<port>";
1406 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1408 GST_DEBUG_OBJECT (nice, "setting stun server, %s", uri_s);
1411 GST_ERROR_OBJECT (nice, "Couldn't parse stun server '%s', %s", uri_s, msg);
1415 if (nice->priv->stun_server)
1416 gst_uri_unref (nice->priv->stun_server);
1417 nice->priv->stun_server = uri;
1421 gst_webrtc_nice_get_stun_server (GstWebRTCICE * ice)
1423 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1424 if (nice->priv->stun_server)
1425 return gst_uri_to_string (nice->priv->stun_server);
1431 gst_webrtc_nice_set_turn_server (GstWebRTCICE * ice, const gchar * uri_s)
1434 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1435 uri = _validate_turn_server (nice, uri_s);
1438 if (nice->priv->turn_server)
1439 gst_uri_unref (nice->priv->turn_server);
1440 nice->priv->turn_server = uri;
1445 gst_webrtc_nice_get_turn_server (GstWebRTCICE * ice)
1447 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1448 if (nice->priv->turn_server)
1449 return gst_uri_to_string (nice->priv->turn_server);
1455 gst_webrtc_nice_set_http_proxy (GstWebRTCICE * ice, const gchar * http_proxy)
1457 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1458 GstUri *uri = _set_http_proxy (ice, http_proxy);
1461 if (nice->priv->http_proxy)
1462 gst_uri_unref (nice->priv->http_proxy);
1463 nice->priv->http_proxy = uri;
1468 gst_webrtc_nice_get_http_proxy (GstWebRTCICE * ice)
1470 GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1472 if (nice->priv->http_proxy)
1473 return gst_uri_to_string (nice->priv->http_proxy);
1479 gst_webrtc_nice_set_property (GObject * object, guint prop_id,
1480 const GValue * value, GParamSpec * pspec)
1482 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1483 GstWebRTCNice *nice = GST_WEBRTC_NICE (object);
1487 g_object_set_property (G_OBJECT (nice->priv->nice_agent),
1491 g_object_set_property (G_OBJECT (nice->priv->nice_agent),
1494 case PROP_MIN_RTP_PORT:
1495 ice->min_rtp_port = g_value_get_uint (value);
1496 if (ice->min_rtp_port > ice->max_rtp_port)
1497 g_warning ("Set min-rtp-port to %u which is larger than"
1498 " max-rtp-port %u", ice->min_rtp_port, ice->max_rtp_port);
1500 case PROP_MAX_RTP_PORT:
1501 ice->max_rtp_port = g_value_get_uint (value);
1502 if (ice->min_rtp_port > ice->max_rtp_port)
1503 g_warning ("Set max-rtp-port to %u which is smaller than"
1504 " min-rtp-port %u", ice->max_rtp_port, ice->min_rtp_port);
1507 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1513 gst_webrtc_nice_get_property (GObject * object, guint prop_id,
1514 GValue * value, GParamSpec * pspec)
1516 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1517 GstWebRTCNice *nice = GST_WEBRTC_NICE (object);
1521 g_value_set_object (value, nice->priv->nice_agent);
1524 g_object_get_property (G_OBJECT (nice->priv->nice_agent),
1528 g_object_get_property (G_OBJECT (nice->priv->nice_agent),
1531 case PROP_MIN_RTP_PORT:
1532 g_value_set_uint (value, ice->min_rtp_port);
1534 case PROP_MAX_RTP_PORT:
1535 g_value_set_uint (value, ice->max_rtp_port);
1538 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1544 gst_webrtc_nice_finalize (GObject * object)
1546 GstWebRTCNice *ice = GST_WEBRTC_NICE (object);
1548 g_signal_handlers_disconnect_by_data (ice->priv->nice_agent, ice);
1552 if (ice->priv->on_candidate_notify)
1553 ice->priv->on_candidate_notify (ice->priv->on_candidate_data);
1554 ice->priv->on_candidate = NULL;
1555 ice->priv->on_candidate_notify = NULL;
1557 if (ice->priv->turn_server)
1558 gst_uri_unref (ice->priv->turn_server);
1559 if (ice->priv->stun_server)
1560 gst_uri_unref (ice->priv->stun_server);
1561 if (ice->priv->http_proxy)
1562 gst_uri_unref (ice->priv->http_proxy);
1564 g_mutex_clear (&ice->priv->lock);
1565 g_cond_clear (&ice->priv->cond);
1567 g_array_free (ice->priv->nice_stream_map, TRUE);
1569 g_object_unref (ice->priv->nice_agent);
1571 g_hash_table_unref (ice->priv->turn_servers);
1573 G_OBJECT_CLASS (parent_class)->finalize (object);
1577 gst_webrtc_nice_constructed (GObject * object)
1579 GstWebRTCNice *ice = GST_WEBRTC_NICE (object);
1580 NiceAgentOption options = 0;
1582 _start_thread (ice);
1584 options |= NICE_AGENT_OPTION_ICE_TRICKLE;
1585 options |= NICE_AGENT_OPTION_REGULAR_NOMINATION;
1587 ice->priv->nice_agent = nice_agent_new_full (ice->priv->main_context,
1588 NICE_COMPATIBILITY_RFC5245, options);
1589 g_signal_connect (ice->priv->nice_agent, "new-candidate-full",
1590 G_CALLBACK (_on_new_candidate), ice);
1592 G_OBJECT_CLASS (parent_class)->constructed (object);
1596 gst_webrtc_nice_class_init (GstWebRTCNiceClass * klass)
1598 GstWebRTCICEClass *gst_webrtc_ice_class = GST_WEBRTC_ICE_CLASS (klass);
1599 GObjectClass *gobject_class = (GObjectClass *) klass;
1601 // override virtual functions
1602 gst_webrtc_ice_class->add_candidate = gst_webrtc_nice_add_candidate;
1603 gst_webrtc_ice_class->add_stream = gst_webrtc_nice_add_stream;
1604 gst_webrtc_ice_class->add_turn_server = gst_webrtc_nice_add_turn_server;
1605 gst_webrtc_ice_class->find_transport = gst_webrtc_nice_find_transport;
1606 gst_webrtc_ice_class->gather_candidates = gst_webrtc_nice_gather_candidates;
1607 gst_webrtc_ice_class->get_is_controller = gst_webrtc_nice_get_is_controller;
1608 gst_webrtc_ice_class->get_stun_server = gst_webrtc_nice_get_stun_server;
1609 gst_webrtc_ice_class->get_turn_server = gst_webrtc_nice_get_turn_server;
1610 gst_webrtc_ice_class->get_http_proxy = gst_webrtc_nice_get_http_proxy;
1611 gst_webrtc_ice_class->set_force_relay = gst_webrtc_nice_set_force_relay;
1612 gst_webrtc_ice_class->set_is_controller = gst_webrtc_nice_set_is_controller;
1613 gst_webrtc_ice_class->set_local_credentials =
1614 gst_webrtc_nice_set_local_credentials;
1615 gst_webrtc_ice_class->set_remote_credentials =
1616 gst_webrtc_nice_set_remote_credentials;
1617 gst_webrtc_ice_class->set_stun_server = gst_webrtc_nice_set_stun_server;
1618 gst_webrtc_ice_class->set_tos = gst_webrtc_nice_set_tos;
1619 gst_webrtc_ice_class->set_turn_server = gst_webrtc_nice_set_turn_server;
1620 gst_webrtc_ice_class->set_http_proxy = gst_webrtc_nice_set_http_proxy;
1621 gst_webrtc_ice_class->set_on_ice_candidate =
1622 gst_webrtc_nice_set_on_ice_candidate;
1623 gst_webrtc_ice_class->get_local_candidates =
1624 gst_webrtc_nice_get_local_candidates;
1625 gst_webrtc_ice_class->get_remote_candidates =
1626 gst_webrtc_nice_get_remote_candidates;
1627 gst_webrtc_ice_class->get_selected_pair = gst_webrtc_nice_get_selected_pair;
1629 gobject_class->constructed = gst_webrtc_nice_constructed;
1630 gobject_class->get_property = gst_webrtc_nice_get_property;
1631 gobject_class->set_property = gst_webrtc_nice_set_property;
1632 gobject_class->finalize = gst_webrtc_nice_finalize;
1634 g_object_class_install_property (gobject_class,
1636 g_param_spec_object ("agent", "ICE agent",
1637 "ICE agent in use by this object. WARNING! Accessing this property "
1638 "may have disastrous consequences for the operation of webrtcbin. "
1639 "Other ICE implementations may not have the same interface.",
1640 NICE_TYPE_AGENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1642 g_object_class_install_property (gobject_class,
1644 g_param_spec_boolean ("ice-tcp", "ICE TCP",
1645 "Whether the agent should use ICE-TCP when gathering candidates",
1646 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1648 g_object_class_install_property (gobject_class,
1650 g_param_spec_boolean ("ice-udp", "ICE UDP",
1651 "Whether the agent should use ICE-UDP when gathering candidates",
1652 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1654 g_signal_override_class_handler ("add-local-ip-address",
1655 G_TYPE_FROM_CLASS (klass),
1656 G_CALLBACK (gst_webrtc_nice_add_local_ip_address));
1660 gst_webrtc_nice_init (GstWebRTCNice * ice)
1662 ice->priv = gst_webrtc_nice_get_instance_private (ice);
1664 g_mutex_init (&ice->priv->lock);
1665 g_cond_init (&ice->priv->cond);
1667 ice->priv->turn_servers =
1668 g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1669 (GDestroyNotify) gst_uri_unref);
1671 ice->priv->nice_stream_map =
1672 g_array_new (FALSE, TRUE, sizeof (struct NiceStreamItem));
1673 g_array_set_clear_func (ice->priv->nice_stream_map,
1674 (GDestroyNotify) _clear_ice_stream);
1678 gst_webrtc_nice_new (const gchar * name)
1680 return g_object_new (GST_TYPE_WEBRTC_NICE, "name", name, NULL);