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 #define GST_CAT_DEFAULT gst_webrtc_ice_debug
36 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
38 #define gst_webrtc_ice_parent_class parent_class
39 G_DEFINE_TYPE_WITH_CODE (GstWebRTCICE, gst_webrtc_ice,
41 GST_DEBUG_CATEGORY_INIT (gst_webrtc_ice_debug, "webrtcice", 0, "webrtcice");
45 gst_webrtc_ice_error_quark (void)
47 return g_quark_from_static_string ("gst-webrtc-ice-error-quark");
53 ON_ICE_CANDIDATE_SIGNAL,
54 ON_ICE_GATHERING_STATE_CHANGE_SIGNAL,
61 PROP_ICE_GATHERING_STATE,
68 static guint gst_webrtc_ice_signals[LAST_SIGNAL] = { 0 };
70 struct _GstWebRTCICEPrivate
72 NiceAgent *nice_agent;
74 GArray *nice_stream_map;
77 GMainContext *main_context;
84 _unlock_pc_thread (GMutex * lock)
86 g_mutex_unlock (lock);
87 return G_SOURCE_REMOVE;
91 _gst_nice_thread (GstWebRTCICE * ice)
93 g_mutex_lock (&ice->priv->lock);
94 ice->priv->main_context = g_main_context_new ();
95 ice->priv->loop = g_main_loop_new (ice->priv->main_context, FALSE);
97 g_cond_broadcast (&ice->priv->cond);
98 g_main_context_invoke (ice->priv->main_context,
99 (GSourceFunc) _unlock_pc_thread, &ice->priv->lock);
101 g_main_loop_run (ice->priv->loop);
103 g_mutex_lock (&ice->priv->lock);
104 g_main_context_unref (ice->priv->main_context);
105 ice->priv->main_context = NULL;
106 g_main_loop_unref (ice->priv->loop);
107 ice->priv->loop = NULL;
108 g_cond_broadcast (&ice->priv->cond);
109 g_mutex_unlock (&ice->priv->lock);
115 _start_thread (GstWebRTCICE * ice)
117 g_mutex_lock (&ice->priv->lock);
118 ice->priv->thread = g_thread_new ("gst-nice-ops",
119 (GThreadFunc) _gst_nice_thread, ice);
121 while (!ice->priv->loop)
122 g_cond_wait (&ice->priv->cond, &ice->priv->lock);
123 g_mutex_unlock (&ice->priv->lock);
127 _stop_thread (GstWebRTCICE * ice)
129 g_mutex_lock (&ice->priv->lock);
130 g_main_loop_quit (ice->priv->loop);
131 while (ice->priv->loop)
132 g_cond_wait (&ice->priv->cond, &ice->priv->lock);
133 g_mutex_unlock (&ice->priv->lock);
135 g_thread_unref (ice->priv->thread);
139 static NiceComponentType
140 _webrtc_component_to_nice (GstWebRTCICEComponent comp)
143 case GST_WEBRTC_ICE_COMPONENT_RTP:
144 return NICE_COMPONENT_TYPE_RTP;
145 case GST_WEBRTC_ICE_COMPONENT_RTCP:
146 return NICE_COMPONENT_TYPE_RTCP;
148 g_assert_not_reached ();
153 static GstWebRTCICEComponent
154 _nice_component_to_webrtc (NiceComponentType comp)
157 case NICE_COMPONENT_TYPE_RTP:
158 return GST_WEBRTC_ICE_COMPONENT_RTP;
159 case NICE_COMPONENT_TYPE_RTCP:
160 return GST_WEBRTC_ICE_COMPONENT_RTCP;
162 g_assert_not_reached ();
167 struct NiceStreamItem
170 guint nice_stream_id;
171 GstWebRTCICEStream *stream;
174 /* TRUE to continue, FALSE to stop */
175 typedef gboolean (*NiceStreamItemForeachFunc) (struct NiceStreamItem * item,
179 _nice_stream_item_foreach (GstWebRTCICE * ice, NiceStreamItemForeachFunc func,
184 len = ice->priv->nice_stream_map->len;
185 for (i = 0; i < len; i++) {
186 struct NiceStreamItem *item =
187 &g_array_index (ice->priv->nice_stream_map, struct NiceStreamItem,
190 if (!func (item, data))
195 /* TRUE for match, FALSE otherwise */
196 typedef gboolean (*NiceStreamItemFindFunc) (struct NiceStreamItem * item,
201 NiceStreamItemFindFunc func;
203 struct NiceStreamItem *ret;
207 _find_nice_item (struct NiceStreamItem *item, gpointer user_data)
209 struct nice_find *f = user_data;
210 if (f->func (item, f->data)) {
217 static struct NiceStreamItem *
218 _nice_stream_item_find (GstWebRTCICE * ice, NiceStreamItemFindFunc func,
227 _nice_stream_item_foreach (ice, _find_nice_item, &f);
232 #define NICE_MATCH_INIT { -1, -1, NULL }
235 _match (struct NiceStreamItem *item, struct NiceStreamItem *m)
237 if (m->session_id != -1 && m->session_id != item->session_id)
239 if (m->nice_stream_id != -1 && m->nice_stream_id != item->nice_stream_id)
241 if (m->stream != NULL && m->stream != item->stream)
247 static struct NiceStreamItem *
248 _find_item (GstWebRTCICE * ice, guint session_id, guint nice_stream_id,
249 GstWebRTCICEStream * stream)
251 struct NiceStreamItem m = NICE_MATCH_INIT;
253 m.session_id = session_id;
254 m.nice_stream_id = nice_stream_id;
257 return _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
260 static struct NiceStreamItem *
261 _create_nice_stream_item (GstWebRTCICE * ice, guint session_id)
263 struct NiceStreamItem item;
265 item.session_id = session_id;
266 item.nice_stream_id = nice_agent_add_stream (ice->priv->nice_agent, 2);
267 item.stream = gst_webrtc_ice_stream_new (ice, item.nice_stream_id);
268 g_array_append_val (ice->priv->nice_stream_map, item);
270 return _find_item (ice, item.session_id, item.nice_stream_id, item.stream);
274 _parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass)
284 colon = g_strstr_len (userinfo, -1, ":");
286 *user = g_strdup (userinfo);
291 *user = g_strndup (userinfo, colon - userinfo);
292 *pass = g_strdup (&colon[1]);
296 gst_webrtc_ice_add_stream (GstWebRTCICE * ice, guint session_id)
298 struct NiceStreamItem m = NICE_MATCH_INIT;
299 struct NiceStreamItem *item;
301 m.session_id = session_id;
302 item = _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
304 GST_ERROR_OBJECT (ice, "stream already added with session_id=%u",
309 item = _create_nice_stream_item (ice, session_id);
311 if (ice->turn_server) {
314 const gchar *userinfo, *transport, *scheme;
315 NiceRelayType relays[4] = { 0, };
318 scheme = gst_uri_get_scheme (ice->turn_server);
319 transport = gst_uri_get_query_value (ice->turn_server, "transport");
320 userinfo = gst_uri_get_userinfo (ice->turn_server);
321 _parse_userinfo (userinfo, &user, &pass);
323 if (g_strcmp0 (scheme, "turns") == 0) {
324 relays[relay_n++] = NICE_RELAY_TYPE_TURN_TLS;
325 } else if (g_strcmp0 (scheme, "turn") == 0) {
326 if (!transport || g_strcmp0 (transport, "udp") == 0)
327 relays[relay_n++] = NICE_RELAY_TYPE_TURN_UDP;
328 if (!transport || g_strcmp0 (transport, "tcp") == 0)
329 relays[relay_n++] = NICE_RELAY_TYPE_TURN_TCP;
331 g_assert (relay_n < G_N_ELEMENTS (relays));
333 for (i = 0; i < relay_n; i++) {
334 ret = nice_agent_set_relay_info (ice->priv->nice_agent,
335 item->nice_stream_id, NICE_COMPONENT_TYPE_RTP,
336 gst_uri_get_host (ice->turn_server),
337 gst_uri_get_port (ice->turn_server), user, pass, relays[i]);
339 gchar *uri = gst_uri_to_string (ice->turn_server);
340 GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
344 ret = nice_agent_set_relay_info (ice->priv->nice_agent,
345 item->nice_stream_id, NICE_COMPONENT_TYPE_RTCP,
346 gst_uri_get_host (ice->turn_server),
347 gst_uri_get_port (ice->turn_server), user, pass, relays[i]);
349 gchar *uri = gst_uri_to_string (ice->turn_server);
350 GST_ERROR_OBJECT (ice, "Failed to set TURN server '%s'", uri);
363 _on_new_candidate (NiceAgent * agent, NiceCandidate * candidate,
366 struct NiceStreamItem *item;
369 item = _find_item (ice, -1, candidate->stream_id, NULL);
371 GST_WARNING_OBJECT (ice, "received signal for non-existent stream %u",
372 candidate->stream_id);
376 if (!candidate->username || !candidate->password) {
377 gboolean got_credentials;
378 gchar *ufrag, *password;
380 got_credentials = nice_agent_get_local_credentials (ice->priv->nice_agent,
381 candidate->stream_id, &ufrag, &password);
382 g_warn_if_fail (got_credentials);
384 if (!candidate->username)
385 candidate->username = ufrag;
389 if (!candidate->password)
390 candidate->password = password;
395 attr = nice_agent_generate_local_candidate_sdp (agent, candidate);
396 g_signal_emit (ice, gst_webrtc_ice_signals[ON_ICE_CANDIDATE_SIGNAL],
397 0, item->session_id, attr);
401 GstWebRTCICETransport *
402 gst_webrtc_ice_find_transport (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
403 GstWebRTCICEComponent component)
405 struct NiceStreamItem *item;
407 item = _find_item (ice, -1, -1, stream);
408 g_return_val_if_fail (item != NULL, NULL);
410 return gst_webrtc_ice_stream_find_transport (item->stream, component);
414 /* TODO don't rely on libnice to (de)serialize candidates */
415 static NiceCandidateType
416 _candidate_type_from_string (const gchar * s)
418 if (g_strcmp0 (s, "host") == 0) {
419 return NICE_CANDIDATE_TYPE_HOST;
420 } else if (g_strcmp0 (s, "srflx") == 0) {
421 return NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
422 } else if (g_strcmp0 (s, "prflx") == 0) { /* FIXME: is the right string? */
423 return NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
424 } else if (g_strcmp0 (s, "relay") == 0) {
425 return NICE_CANDIDATE_TYPE_RELAY;
427 g_assert_not_reached ();
433 _candidate_type_to_string (NiceCandidateType type)
436 case NICE_CANDIDATE_TYPE_HOST:
438 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
440 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
442 case NICE_CANDIDATE_TYPE_RELAY:
445 g_assert_not_reached ();
450 static NiceCandidateTransport
451 _candidate_transport_from_string (const gchar * s)
453 if (g_strcmp0 (s, "UDP") == 0) {
454 return NICE_CANDIDATE_TRANSPORT_UDP;
455 } else if (g_strcmp0 (s, "TCP tcptype") == 0) {
456 return NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
457 } else if (g_strcmp0 (s, "tcp-passive") == 0) { /* FIXME: is the right string? */
458 return NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
459 } else if (g_strcmp0 (s, "tcp-so") == 0) {
460 return NICE_CANDIDATE_TRANSPORT_TCP_SO;
462 g_assert_not_reached ();
468 _candidate_type_to_string (NiceCandidateType type)
471 case NICE_CANDIDATE_TYPE_HOST:
473 case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
475 case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
477 case NICE_CANDIDATE_TYPE_RELAY:
480 g_assert_not_reached ();
486 /* must start with "a=candidate:" */
488 gst_webrtc_ice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
489 const gchar * candidate)
491 struct NiceStreamItem *item;
493 GSList *candidates = NULL;
495 item = _find_item (ice, -1, -1, stream);
496 g_return_if_fail (item != NULL);
499 nice_agent_parse_remote_candidate_sdp (ice->priv->nice_agent,
500 item->nice_stream_id, candidate);
502 GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'", candidate);
506 candidates = g_slist_append (candidates, cand);
508 nice_agent_set_remote_candidates (ice->priv->nice_agent, item->nice_stream_id,
509 cand->component_id, candidates);
511 g_slist_free (candidates);
512 nice_candidate_free (cand);
516 gst_webrtc_ice_set_remote_credentials (GstWebRTCICE * ice,
517 GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
519 struct NiceStreamItem *item;
521 g_return_val_if_fail (ufrag != NULL, FALSE);
522 g_return_val_if_fail (pwd != NULL, FALSE);
523 item = _find_item (ice, -1, -1, stream);
524 g_return_val_if_fail (item != NULL, FALSE);
526 GST_DEBUG_OBJECT (ice, "Setting remote ICE credentials on "
527 "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
529 nice_agent_set_remote_credentials (ice->priv->nice_agent,
530 item->nice_stream_id, ufrag, pwd);
536 gst_webrtc_ice_set_local_credentials (GstWebRTCICE * ice,
537 GstWebRTCICEStream * stream, gchar * ufrag, gchar * pwd)
539 struct NiceStreamItem *item;
541 g_return_val_if_fail (ufrag != NULL, FALSE);
542 g_return_val_if_fail (pwd != NULL, FALSE);
543 item = _find_item (ice, -1, -1, stream);
544 g_return_val_if_fail (item != NULL, FALSE);
546 GST_DEBUG_OBJECT (ice, "Setting local ICE credentials on "
547 "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
549 nice_agent_set_local_credentials (ice->priv->nice_agent, item->nice_stream_id,
556 gst_webrtc_ice_gather_candidates (GstWebRTCICE * ice,
557 GstWebRTCICEStream * stream)
559 struct NiceStreamItem *item;
561 item = _find_item (ice, -1, -1, stream);
562 g_return_val_if_fail (item != NULL, FALSE);
564 GST_DEBUG_OBJECT (ice, "gather candidates for stream %u",
565 item->nice_stream_id);
567 return gst_webrtc_ice_stream_gather_candidates (stream);
571 _clear_ice_stream (struct NiceStreamItem *item)
577 g_signal_handlers_disconnect_by_data (item->stream->ice->priv->nice_agent,
579 gst_object_unref (item->stream);
584 _resolve_host (const gchar * host)
586 GResolver *resolver = g_resolver_get_default ();
587 GError *error = NULL;
591 if (!(addresses = g_resolver_lookup_by_name (resolver, host, NULL, &error))) {
592 GST_ERROR ("%s", error->message);
593 g_clear_error (&error);
597 /* XXX: only the first address is used */
598 addr = addresses->data;
600 return g_inet_address_to_string (addr);
604 _set_turn_server (GstWebRTCICE * ice, const gchar * s)
606 GstUri *uri = gst_uri_from_string (s);
607 const gchar *userinfo, *host, *scheme;
608 GList *keys = NULL, *l;
609 gchar *ip = NULL, *user = NULL, *pass = NULL;
610 gboolean turn_tls = FALSE;
613 GST_DEBUG_OBJECT (ice, "setting turn server, %s", s);
616 GST_ERROR_OBJECT (ice, "Could not parse turn server '%s'", s);
620 scheme = gst_uri_get_scheme (uri);
621 if (g_strcmp0 (scheme, "turn") == 0) {
622 } else if (g_strcmp0 (scheme, "turns") == 0) {
625 GST_ERROR_OBJECT (ice, "unknown scheme '%s'", scheme);
629 keys = gst_uri_get_query_keys (uri);
630 for (l = keys; l; l = l->next) {
631 gchar *key = l->data;
633 if (g_strcmp0 (key, "transport") == 0) {
634 const gchar *transport = gst_uri_get_query_value (uri, "transport");
636 } else if (g_strcmp0 (transport, "udp") == 0) {
637 } else if (g_strcmp0 (transport, "tcp") == 0) {
639 GST_ERROR_OBJECT (ice, "unknown transport value, '%s'", transport);
643 GST_ERROR_OBJECT (ice, "unknown query key, '%s'", key);
648 /* TODO: Implement error checking similar to the stun server below */
649 userinfo = gst_uri_get_userinfo (uri);
650 _parse_userinfo (userinfo, &user, &pass);
652 GST_ERROR_OBJECT (ice, "No username specified in '%s'", s);
656 GST_ERROR_OBJECT (ice, "No password specified in '%s'", s);
660 host = gst_uri_get_host (uri);
662 GST_ERROR_OBJECT (ice, "Turn server has no host");
665 ip = _resolve_host (host);
667 GST_ERROR_OBJECT (ice, "Failed to resolve turn server '%s'", host);
670 port = gst_uri_get_port (uri);
672 if (port == GST_URI_NO_PORT) {
674 gst_uri_set_port (uri, 5349);
676 gst_uri_set_port (uri, 3478);
679 /* Set the resolved IP as the host since that's what libnice wants */
680 gst_uri_set_host (uri, ip);
682 if (ice->turn_server)
683 gst_uri_unref (ice->turn_server);
684 ice->turn_server = uri;
694 gst_webrtc_ice_set_property (GObject * object, guint prop_id,
695 const GValue * value, GParamSpec * pspec)
697 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
700 case PROP_STUN_SERVER:{
701 const gchar *s = g_value_get_string (value);
702 GstUri *uri = gst_uri_from_string (s);
703 const gchar *msg = "must be of the form stun://<host>:<port>";
708 GST_DEBUG_OBJECT (ice, "setting stun server, %s", s);
711 GST_ERROR_OBJECT (ice, "Couldn't parse stun server '%s', %s", s, msg);
715 host = gst_uri_get_host (uri);
717 GST_ERROR_OBJECT (ice, "Stun server '%s' has no host, %s", s, msg);
720 port = gst_uri_get_port (uri);
721 if (port == GST_URI_NO_PORT) {
722 GST_INFO_OBJECT (ice, "Stun server '%s' has no port, assuming 3478", s);
724 gst_uri_set_port (uri, port);
727 ip = _resolve_host (host);
729 GST_ERROR_OBJECT (ice, "Failed to resolve stun server '%s'", host);
733 if (ice->stun_server)
734 gst_uri_unref (ice->stun_server);
735 ice->stun_server = uri;
737 g_object_set (ice->priv->nice_agent, "stun-server", ip,
738 "stun-server-port", port, NULL);
743 case PROP_TURN_SERVER:{
744 _set_turn_server (ice, g_value_get_string (value));
747 case PROP_CONTROLLER:
748 g_object_set_property (G_OBJECT (ice->priv->nice_agent),
749 "controlling-mode", value);
752 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
758 gst_webrtc_ice_get_property (GObject * object, guint prop_id,
759 GValue * value, GParamSpec * pspec)
761 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
764 case PROP_STUN_SERVER:
765 if (ice->stun_server)
766 g_value_take_string (value, gst_uri_to_string (ice->stun_server));
768 g_value_take_string (value, NULL);
770 case PROP_TURN_SERVER:
771 if (ice->turn_server)
772 g_value_take_string (value, gst_uri_to_string (ice->turn_server));
774 g_value_take_string (value, NULL);
776 case PROP_CONTROLLER:
777 g_object_get_property (G_OBJECT (ice->priv->nice_agent),
778 "controlling-mode", value);
781 g_value_set_object (value, ice->priv->nice_agent);
784 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
790 gst_webrtc_ice_finalize (GObject * object)
792 GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
794 g_signal_handlers_disconnect_by_data (ice->priv->nice_agent, ice);
798 if (ice->turn_server)
799 gst_uri_unref (ice->turn_server);
800 if (ice->stun_server)
801 gst_uri_unref (ice->stun_server);
803 g_mutex_clear (&ice->priv->lock);
804 g_cond_clear (&ice->priv->cond);
806 g_array_free (ice->priv->nice_stream_map, TRUE);
808 g_object_unref (ice->priv->nice_agent);
810 G_OBJECT_CLASS (parent_class)->finalize (object);
814 gst_webrtc_ice_class_init (GstWebRTCICEClass * klass)
816 GObjectClass *gobject_class = (GObjectClass *) klass;
818 g_type_class_add_private (klass, sizeof (GstWebRTCICEPrivate));
820 gobject_class->get_property = gst_webrtc_ice_get_property;
821 gobject_class->set_property = gst_webrtc_ice_set_property;
822 gobject_class->finalize = gst_webrtc_ice_finalize;
824 g_object_class_install_property (gobject_class,
826 g_param_spec_string ("stun-server", "STUN Server",
827 "The STUN server of the form stun://hostname:port",
828 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
830 g_object_class_install_property (gobject_class,
832 g_param_spec_string ("turn-server", "TURN Server",
833 "The TURN server of the form turn(s)://username:password@host:port",
834 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
836 g_object_class_install_property (gobject_class,
838 g_param_spec_boolean ("controller", "ICE controller",
839 "Whether the ICE agent is the controller or controlled. "
840 "In WebRTC, the initial offerrer is the ICE controller.", FALSE,
841 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
843 g_object_class_install_property (gobject_class,
845 g_param_spec_object ("agent", "ICE agent",
846 "ICE agent in use by this object", NICE_TYPE_AGENT,
847 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
850 * GstWebRTCICE::on-ice-candidate:
851 * @object: the #GstWebRtcBin
852 * @candidate: the ICE candidate
854 gst_webrtc_ice_signals[ON_ICE_CANDIDATE_SIGNAL] =
855 g_signal_new ("on-ice-candidate", G_TYPE_FROM_CLASS (klass),
856 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
857 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
861 gst_webrtc_ice_init (GstWebRTCICE * ice)
864 G_TYPE_INSTANCE_GET_PRIVATE ((ice), GST_TYPE_WEBRTC_ICE,
865 GstWebRTCICEPrivate);
867 g_mutex_init (&ice->priv->lock);
868 g_cond_init (&ice->priv->cond);
872 ice->priv->nice_agent = nice_agent_new (ice->priv->main_context,
873 NICE_COMPATIBILITY_RFC5245);
874 g_signal_connect (ice->priv->nice_agent, "new-candidate-full",
875 G_CALLBACK (_on_new_candidate), ice);
877 ice->priv->nice_stream_map =
878 g_array_new (FALSE, TRUE, sizeof (struct NiceStreamItem));
879 g_array_set_clear_func (ice->priv->nice_stream_map,
880 (GDestroyNotify) _clear_ice_stream);
884 gst_webrtc_ice_new (void)
886 return g_object_new (GST_TYPE_WEBRTC_ICE, NULL);