Merge branch 'upstream/1.22.7' into tizen_gst_1.22.7
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / webrtc / nice / nice.c
1 /* GStreamer
2  * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "nice.h"
25 #include "nicestream.h"
26 /* libnice */
27 #include <agent.h>
28
29 #define HTTP_PROXY_PORT_DEFAULT 3128
30
31 /* XXX:
32  *
33  * - are locally generated remote candidates meant to be readded to libnice?
34  */
35
36 static GstUri *_validate_turn_server (GstWebRTCNice * ice, const gchar * s);
37
38 #define GST_CAT_DEFAULT gst_webrtc_nice_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
40
41 enum
42 {
43   PROP_0,
44   PROP_AGENT,
45   PROP_ICE_TCP,
46   PROP_ICE_UDP,
47   PROP_MIN_RTP_PORT,
48   PROP_MAX_RTP_PORT,
49 };
50
51 struct _GstWebRTCNicePrivate
52 {
53   NiceAgent *nice_agent;
54
55   GArray *nice_stream_map;
56
57   GThread *thread;
58   GMainContext *main_context;
59   GMainLoop *loop;
60   GMutex lock;
61   GCond cond;
62
63   GstWebRTCICEOnCandidateFunc on_candidate;
64   gpointer on_candidate_data;
65   GDestroyNotify on_candidate_notify;
66
67   GstUri *stun_server;
68   GstUri *turn_server;
69
70   GHashTable *turn_servers;
71
72   GstUri *http_proxy;
73 };
74
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,
79         "webrtcnice"););
80
81 static gboolean
82 _unlock_pc_thread (GMutex * lock)
83 {
84   g_mutex_unlock (lock);
85   return G_SOURCE_REMOVE;
86 }
87
88 static gpointer
89 _gst_nice_thread (GstWebRTCNice * ice)
90 {
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);
94
95   g_cond_broadcast (&ice->priv->cond);
96   g_main_context_invoke (ice->priv->main_context,
97       (GSourceFunc) _unlock_pc_thread, &ice->priv->lock);
98
99   g_main_loop_run (ice->priv->loop);
100
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);
108
109   return NULL;
110 }
111
112 static void
113 _start_thread (GstWebRTCNice * ice)
114 {
115   g_mutex_lock (&ice->priv->lock);
116   ice->priv->thread = g_thread_new (GST_OBJECT_NAME (ice),
117       (GThreadFunc) _gst_nice_thread, ice);
118
119   while (!ice->priv->loop)
120     g_cond_wait (&ice->priv->cond, &ice->priv->lock);
121   g_mutex_unlock (&ice->priv->lock);
122 }
123
124 static void
125 _stop_thread (GstWebRTCNice * ice)
126 {
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);
132
133   g_thread_unref (ice->priv->thread);
134 }
135
136 struct NiceStreamItem
137 {
138   guint session_id;
139   guint nice_stream_id;
140   GstWebRTCICEStream *stream;
141 };
142
143 /* TRUE to continue, FALSE to stop */
144 typedef gboolean (*NiceStreamItemForeachFunc) (struct NiceStreamItem * item,
145     gpointer user_data);
146
147 static void
148 _nice_stream_item_foreach (GstWebRTCNice * ice, NiceStreamItemForeachFunc func,
149     gpointer data)
150 {
151   int i, len;
152
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,
157         i);
158
159     if (!func (item, data))
160       break;
161   }
162 }
163
164 /* TRUE for match, FALSE otherwise */
165 typedef gboolean (*NiceStreamItemFindFunc) (struct NiceStreamItem * item,
166     gpointer user_data);
167
168 struct nice_find
169 {
170   NiceStreamItemFindFunc func;
171   gpointer data;
172   struct NiceStreamItem *ret;
173 };
174
175 static gboolean
176 _find_nice_item (struct NiceStreamItem *item, gpointer user_data)
177 {
178   struct nice_find *f = user_data;
179   if (f->func (item, f->data)) {
180     f->ret = item;
181     return FALSE;
182   }
183   return TRUE;
184 }
185
186 static struct NiceStreamItem *
187 _nice_stream_item_find (GstWebRTCNice * ice, NiceStreamItemFindFunc func,
188     gpointer data)
189 {
190   struct nice_find f;
191
192   f.func = func;
193   f.data = data;
194   f.ret = NULL;
195
196   _nice_stream_item_foreach (ice, _find_nice_item, &f);
197
198   return f.ret;
199 }
200
201 #define NICE_MATCH_INIT { -1, -1, NULL }
202
203 static gboolean
204 _match (struct NiceStreamItem *item, struct NiceStreamItem *m)
205 {
206   if (m->session_id != -1 && m->session_id != item->session_id)
207     return FALSE;
208   if (m->nice_stream_id != -1 && m->nice_stream_id != item->nice_stream_id)
209     return FALSE;
210   if (m->stream != NULL && m->stream != item->stream)
211     return FALSE;
212
213   return TRUE;
214 }
215
216 static struct NiceStreamItem *
217 _find_item (GstWebRTCNice * ice, guint session_id, guint nice_stream_id,
218     GstWebRTCICEStream * stream)
219 {
220   struct NiceStreamItem m = NICE_MATCH_INIT;
221
222   m.session_id = session_id;
223   m.nice_stream_id = nice_stream_id;
224   m.stream = stream;
225
226   return _nice_stream_item_find (ice, (NiceStreamItemFindFunc) _match, &m);
227 }
228
229 static struct NiceStreamItem *
230 _create_nice_stream_item (GstWebRTCNice * ice, guint session_id)
231 {
232   struct NiceStreamItem item;
233
234   item.session_id = session_id;
235   item.nice_stream_id = nice_agent_add_stream (ice->priv->nice_agent, 1);
236   item.stream =
237       GST_WEBRTC_ICE_STREAM (gst_webrtc_nice_stream_new (GST_WEBRTC_ICE (ice),
238           item.nice_stream_id)
239       );
240
241   g_array_append_val (ice->priv->nice_stream_map, item);
242
243   return _find_item (ice, item.session_id, item.nice_stream_id, item.stream);
244 }
245
246 static void
247 _parse_userinfo (const gchar * userinfo, gchar ** user, gchar ** pass)
248 {
249   const gchar *colon;
250
251   if (!userinfo) {
252     *user = NULL;
253     *pass = NULL;
254     return;
255   }
256
257   colon = g_strstr_len (userinfo, -1, ":");
258   if (!colon) {
259     *user = g_uri_unescape_string (userinfo, NULL);
260     *pass = NULL;
261     return;
262   }
263
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);
269
270   *user = g_uri_unescape_segment (userinfo, colon, NULL);
271   *pass = g_uri_unescape_string (&colon[1], NULL);
272 }
273
274 struct resolve_host_data
275 {
276   GstWebRTCNice *ice;
277   char *host;
278   gboolean main_context_handled;
279   gpointer user_data;
280   GDestroyNotify notify;
281 };
282
283 static void
284 on_resolve_host (GResolver * resolver, GAsyncResult * res, gpointer user_data)
285 {
286   GTask *task = user_data;
287   struct resolve_host_data *rh;
288   GError *error = NULL;
289   GList *addresses;
290
291   rh = g_task_get_task_data (task);
292
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);
297     return;
298   }
299
300   GST_DEBUG_OBJECT (rh->ice, "Resolved %d addresses for host %s with data %p",
301       g_list_length (addresses), rh->host, rh);
302
303   g_task_return_pointer (task, addresses,
304       (GDestroyNotify) g_resolver_free_addresses);
305   g_object_unref (task);
306 }
307
308 static void
309 free_resolve_host_data (struct resolve_host_data *rh)
310 {
311   GST_TRACE_OBJECT (rh->ice, "Freeing data %p for resolving host %s", rh,
312       rh->host);
313
314   if (rh->notify)
315     rh->notify (rh->user_data);
316
317   g_free (rh->host);
318   g_free (rh);
319 }
320
321 static struct resolve_host_data *
322 resolve_host_data_new (GstWebRTCNice * ice, const char *host)
323 {
324   struct resolve_host_data *rh = g_new0 (struct resolve_host_data, 1);
325
326   rh->ice = ice;
327   rh->host = g_strdup (host);
328
329   return rh;
330 }
331
332 static gboolean
333 resolve_host_main_cb (gpointer user_data)
334 {
335   GResolver *resolver = g_resolver_get_default ();
336   GTask *task = user_data;
337   struct resolve_host_data *rh;
338
339   rh = g_task_get_task_data (task);
340   /* no need to error anymore if the main context disappears and this task is
341    * not run */
342   rh->main_context_handled = TRUE;
343
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));
347
348   return G_SOURCE_REMOVE;
349 }
350
351 static void
352 error_task_if_unhandled (GTask * task)
353 {
354   struct resolve_host_data *rh;
355
356   rh = g_task_get_task_data (task);
357
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",
362         "Cancelled");
363   }
364
365   g_object_unref (task);
366 }
367
368 static void
369 resolve_host_async (GstWebRTCNice * ice, const gchar * host,
370     GAsyncReadyCallback cb, gpointer user_data, GDestroyNotify notify)
371 {
372   struct resolve_host_data *rh = resolve_host_data_new (ice, host);
373   GTask *task;
374
375   rh->user_data = user_data;
376   rh->notify = notify;
377   task = g_task_new (rh->ice, NULL, cb, user_data);
378
379   g_task_set_task_data (task, rh, (GDestroyNotify) free_resolve_host_data);
380
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);
385 }
386
387 static GList *
388 resolve_host_finish (GstWebRTCNice * ice, GAsyncResult * res, GError ** error)
389 {
390   g_return_val_if_fail (g_task_is_valid (res, ice), NULL);
391
392   return g_task_propagate_pointer (G_TASK (res), error);
393 }
394
395 static void
396 _add_turn_server (GstWebRTCNice * ice, struct NiceStreamItem *item,
397     GstUri * turn_server)
398 {
399   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
400   const gchar *host;
401   NiceRelayType relays[4] = { 0, };
402   gchar *user, *pass;
403   const gchar *userinfo, *transport, *scheme;
404   int i, relay_n = 0;
405
406   host = gst_uri_get_host (turn_server);
407   if (!host) {
408     GST_ERROR_OBJECT (ice, "Turn server has no host");
409     return;
410   }
411
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);
416
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;
424   }
425   g_assert (relay_n < G_N_ELEMENTS (relays));
426
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",
434           uri_str);
435       g_free (uri_str);
436     }
437   }
438
439   g_free (user);
440   g_free (pass);
441
442 }
443
444 typedef struct
445 {
446   GstWebRTCNice *ice;
447   struct NiceStreamItem *item;
448 } AddTurnServerData;
449
450 static void
451 _add_turn_server_func (const gchar * uri, GstUri * turn_server,
452     AddTurnServerData * data)
453 {
454   _add_turn_server (data->ice, data->item, turn_server);
455 }
456
457 static void
458 _add_stun_server (GstWebRTCNice * ice, GstUri * stun_server)
459 {
460   const gchar *msg = "must be of the form stun://<host>:<port>";
461   const gchar *host;
462   gchar *s = NULL;
463   guint port;
464
465   s = gst_uri_to_string (stun_server);
466   GST_DEBUG_OBJECT (ice, "adding stun server, %s", s);
467
468   host = gst_uri_get_host (stun_server);
469   if (!host) {
470     GST_ERROR_OBJECT (ice, "Stun server '%s' has no host, %s", s, msg);
471     goto out;
472   }
473
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);
477     port = 3478;
478     gst_uri_set_port (stun_server, port);
479   }
480
481   g_object_set (ice->priv->nice_agent, "stun-server", host,
482       "stun-server-port", port, NULL);
483
484 out:
485   g_free (s);
486 }
487
488 static GstWebRTCICEStream *
489 gst_webrtc_nice_add_stream (GstWebRTCICE * ice, guint session_id)
490 {
491   struct NiceStreamItem m = NICE_MATCH_INIT;
492   struct NiceStreamItem *item;
493   AddTurnServerData add_data;
494   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
495
496   m.session_id = session_id;
497   item = _nice_stream_item_find (nice, (NiceStreamItemFindFunc) _match, &m);
498   if (item) {
499     GST_ERROR_OBJECT (nice, "stream already added with session_id=%u",
500         session_id);
501     return 0;
502   }
503
504   if (nice->priv->stun_server) {
505     _add_stun_server (nice, nice->priv->stun_server);
506   }
507
508   item = _create_nice_stream_item (nice, session_id);
509
510   if (nice->priv->turn_server) {
511     _add_turn_server (nice, item, nice->priv->turn_server);
512   }
513
514   add_data.ice = nice;
515   add_data.item = item;
516
517   g_hash_table_foreach (nice->priv->turn_servers,
518       (GHFunc) _add_turn_server_func, &add_data);
519
520   return item->stream;
521 }
522
523 static void
524 _on_new_candidate (NiceAgent * agent, NiceCandidate * candidate,
525     GstWebRTCNice * ice)
526 {
527   struct NiceStreamItem *item;
528   gchar *attr;
529
530   item = _find_item (ice, -1, candidate->stream_id, NULL);
531   if (!item) {
532     GST_WARNING_OBJECT (ice, "received signal for non-existent stream %u",
533         candidate->stream_id);
534     return;
535   }
536
537   if (!candidate->username || !candidate->password) {
538     gboolean got_credentials;
539     gchar *ufrag, *password;
540
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);
544
545     if (!candidate->username)
546       candidate->username = ufrag;
547     else
548       g_free (ufrag);
549
550     if (!candidate->password)
551       candidate->password = password;
552     else
553       g_free (password);
554   }
555
556   attr = nice_agent_generate_local_candidate_sdp (agent, candidate);
557
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);
561
562   g_free (attr);
563 }
564
565 static GstWebRTCICETransport *
566 gst_webrtc_nice_find_transport (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
567     GstWebRTCICEComponent component)
568 {
569   struct NiceStreamItem *item;
570   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
571
572   item = _find_item (nice, -1, -1, stream);
573   g_return_val_if_fail (item != NULL, NULL);
574
575   return gst_webrtc_ice_stream_find_transport (item->stream, component);
576 }
577
578 #if 0
579 /* TODO don't rely on libnice to (de)serialize candidates */
580 static NiceCandidateType
581 _candidate_type_from_string (const gchar * s)
582 {
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;
591   } else {
592     g_assert_not_reached ();
593     return 0;
594   }
595 }
596
597 static const gchar *
598 _candidate_type_to_string (NiceCandidateType type)
599 {
600   switch (type) {
601     case NICE_CANDIDATE_TYPE_HOST:
602       return "host";
603     case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
604       return "srflx";
605     case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
606       return "prflx";
607     case NICE_CANDIDATE_TYPE_RELAY:
608       return "relay";
609     default:
610       g_assert_not_reached ();
611       return NULL;
612   }
613 }
614
615 static NiceCandidateTransport
616 _candidate_transport_from_string (const gchar * s)
617 {
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;
626   } else {
627     g_assert_not_reached ();
628     return 0;
629   }
630 }
631
632 static const gchar *
633 _candidate_type_to_string (NiceCandidateType type)
634 {
635   switch (type) {
636     case NICE_CANDIDATE_TYPE_HOST:
637       return "host";
638     case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
639       return "srflx";
640     case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
641       return "prflx";
642     case NICE_CANDIDATE_TYPE_RELAY:
643       return "relay";
644     default:
645       g_assert_not_reached ();
646       return NULL;
647   }
648 }
649 #endif
650
651 /* parse the address for possible resolution */
652 static gboolean
653 get_candidate_address (const gchar * candidate, gchar ** prefix,
654     gchar ** address, gchar ** postfix)
655 {
656   char **tokens = NULL;
657   char *tmp_address = NULL;
658
659   if (!g_str_has_prefix (candidate, "a=candidate:")) {
660     GST_ERROR ("candidate \"%s\" does not start with \"a=candidate:\"",
661         candidate);
662     goto failure;
663   }
664
665   if (!(tokens = g_strsplit (candidate, " ", 6))) {
666     GST_ERROR ("candidate \"%s\" could not be tokenized", candidate);
667     goto failure;
668   }
669
670   if (g_strv_length (tokens) < 6) {
671     GST_ERROR ("candidate \"%s\" tokenization resulted in not enough tokens",
672         candidate);
673     goto failure;
674   }
675
676   tmp_address = tokens[4];
677   if (address)
678     *address = g_strdup (tmp_address);
679   tokens[4] = NULL;
680
681   if (prefix)
682     *prefix = g_strjoinv (" ", tokens);
683   if (postfix)
684     *postfix = g_strdup (tokens[5]);
685
686   tokens[4] = tmp_address;
687
688   g_strfreev (tokens);
689   return TRUE;
690
691 failure:
692   if (tokens)
693     g_strfreev (tokens);
694   return FALSE;
695 }
696
697 struct resolve_candidate_data
698 {
699   guint nice_stream_id;
700   char *prefix;
701   char *postfix;
702 };
703
704 static void
705 free_resolve_candidate_data (struct resolve_candidate_data *rc)
706 {
707   g_free (rc->prefix);
708   g_free (rc->postfix);
709   g_free (rc);
710 }
711
712 static void
713 add_ice_candidate_to_libnice (GstWebRTCICE * ice, guint nice_stream_id,
714     NiceCandidate * cand)
715 {
716   GSList *candidates = NULL;
717   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
718
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");
722     return;
723   }
724
725   candidates = g_slist_append (candidates, cand);
726
727   nice_agent_set_remote_candidates (nice->priv->nice_agent, nice_stream_id,
728       cand->component_id, candidates);
729
730   g_slist_free (candidates);
731 }
732
733 static void
734 on_candidate_resolved (GstWebRTCICE * ice, GAsyncResult * res,
735     gpointer user_data)
736 {
737   struct resolve_candidate_data *rc = user_data;
738   GError *error = NULL;
739   GList *addresses;
740   char *new_candv[4] = { NULL, };
741   char *new_addr, *new_candidate;
742   NiceCandidate *cand;
743   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
744
745   if (!(addresses = resolve_host_finish (nice, res, &error))) {
746     GST_WARNING_OBJECT (ice, "Could not resolve candidate address: %s",
747         error->message);
748     g_clear_error (&error);
749     return;
750   }
751
752   new_addr = g_inet_address_to_string (addresses->data);
753   g_resolver_free_addresses (addresses);
754   addresses = NULL;
755
756   new_candv[0] = rc->prefix;
757   new_candv[1] = new_addr;
758   new_candv[2] = rc->postfix;
759   new_candv[3] = NULL;
760   new_candidate = g_strjoinv (" ", new_candv);
761
762   GST_DEBUG_OBJECT (ice, "resolved to candidate %s", new_candidate);
763
764   cand =
765       nice_agent_parse_remote_candidate_sdp (nice->priv->nice_agent,
766       rc->nice_stream_id, new_candidate);
767   g_free (new_candidate);
768   if (!cand) {
769 #ifdef TIZEN_FEATURE_BUG_FIX
770     GST_WARNING_OBJECT (ice, "Could not parse candidate");
771 #else
772     GST_WARNING_OBJECT (ice, "Could not parse candidate \'%s\'", new_candidate);
773 #endif
774     return;
775   }
776
777   g_free (new_addr);
778
779   add_ice_candidate_to_libnice (ice, rc->nice_stream_id, cand);
780   nice_candidate_free (cand);
781 }
782
783 /* candidate must start with "a=candidate:" or be NULL*/
784 static void
785 gst_webrtc_nice_add_candidate (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
786     const gchar * candidate)
787 {
788   struct NiceStreamItem *item;
789   NiceCandidate *cand;
790   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
791
792   item = _find_item (nice, -1, -1, stream);
793   g_return_if_fail (item != NULL);
794
795   if (candidate == NULL) {
796     nice_agent_peer_candidate_gathering_done (nice->priv->nice_agent,
797         item->nice_stream_id);
798     return;
799   }
800
801   cand =
802       nice_agent_parse_remote_candidate_sdp (nice->priv->nice_agent,
803       item->nice_stream_id, candidate);
804   if (!cand) {
805     /* might be a .local candidate */
806     char *prefix = NULL, *address = NULL, *postfix = NULL;
807     struct resolve_candidate_data *rc;
808
809     if (!get_candidate_address (candidate, &prefix, &address, &postfix)) {
810       GST_WARNING_OBJECT (nice, "Failed to retrieve address from candidate %s",
811           candidate);
812       goto done;
813     }
814
815     if (!g_str_has_suffix (address, ".local")) {
816       GST_WARNING_OBJECT (nice, "candidate address \'%s\' does not end "
817           "with \'.local\'", address);
818       goto done;
819     }
820
821     rc = g_new0 (struct resolve_candidate_data, 1);
822     rc->nice_stream_id = item->nice_stream_id;
823     rc->prefix = prefix;
824     rc->postfix = postfix;
825     resolve_host_async (nice, address,
826         (GAsyncReadyCallback) on_candidate_resolved, rc,
827         (GDestroyNotify) free_resolve_candidate_data);
828
829     prefix = NULL;
830     postfix = NULL;
831
832   done:
833     g_clear_pointer (&address, g_free);
834     g_clear_pointer (&prefix, g_free);
835     g_clear_pointer (&postfix, g_free);
836
837     return;
838   }
839
840   add_ice_candidate_to_libnice (ice, item->nice_stream_id, cand);
841   nice_candidate_free (cand);
842 }
843
844 static gboolean
845 gst_webrtc_nice_set_remote_credentials (GstWebRTCICE * ice,
846     GstWebRTCICEStream * stream, const gchar * ufrag, const gchar * pwd)
847 {
848   struct NiceStreamItem *item;
849   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
850
851   g_return_val_if_fail (ufrag != NULL, FALSE);
852   g_return_val_if_fail (pwd != NULL, FALSE);
853   item = _find_item (nice, -1, -1, stream);
854   g_return_val_if_fail (item != NULL, FALSE);
855
856   GST_DEBUG_OBJECT (nice, "Setting remote ICE credentials on "
857       "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
858
859   nice_agent_set_remote_credentials (nice->priv->nice_agent,
860       item->nice_stream_id, ufrag, pwd);
861
862   return TRUE;
863 }
864
865 typedef struct
866 {
867   GstWebRTCNice *ice;
868   GstUri *turn_server;
869 } AddTurnServerToStreamData;
870
871 static gboolean
872 _add_turn_server_foreach_stream_func (struct NiceStreamItem *item,
873     gpointer data)
874 {
875   AddTurnServerToStreamData *add_data = (AddTurnServerToStreamData *) data;
876   _add_turn_server (add_data->ice, item, add_data->turn_server);
877   return TRUE;
878 }
879
880 static gboolean
881 gst_webrtc_nice_add_turn_server (GstWebRTCICE * ice, const gchar * uri)
882 {
883   gboolean ret = FALSE;
884   GstUri *valid_uri;
885   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
886   gboolean inserted;
887   AddTurnServerToStreamData add_data;
888
889   if (!(valid_uri = _validate_turn_server (nice, uri)))
890     goto done;
891
892   inserted =
893       g_hash_table_insert (nice->priv->turn_servers, g_strdup (uri), valid_uri);
894
895   /* add the turn server to any streams that were already created */
896   if (inserted) {
897     add_data.ice = nice;
898     add_data.turn_server = valid_uri;
899     _nice_stream_item_foreach (nice, _add_turn_server_foreach_stream_func,
900         &add_data);
901   }
902
903   ret = TRUE;
904
905 done:
906   return ret;
907 }
908
909 static gboolean
910 gst_webrtc_nice_add_local_ip_address (GstWebRTCNice * ice,
911     const gchar * address)
912 {
913   gboolean ret = FALSE;
914   NiceAddress nice_addr;
915
916   nice_address_init (&nice_addr);
917
918   ret = nice_address_set_from_string (&nice_addr, address);
919
920   if (ret) {
921     ret = nice_agent_add_local_address (ice->priv->nice_agent, &nice_addr);
922     if (!ret) {
923       GST_ERROR_OBJECT (ice, "Failed to add local address to NiceAgent");
924     }
925   } else {
926     GST_ERROR_OBJECT (ice, "Failed to initialize NiceAddress [%s]", address);
927   }
928
929   return ret;
930 }
931
932 static gboolean
933 gst_webrtc_nice_set_local_credentials (GstWebRTCICE * ice,
934     GstWebRTCICEStream * stream, const gchar * ufrag, const gchar * pwd)
935 {
936   struct NiceStreamItem *item;
937   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
938
939   g_return_val_if_fail (ufrag != NULL, FALSE);
940   g_return_val_if_fail (pwd != NULL, FALSE);
941   item = _find_item (nice, -1, -1, stream);
942   g_return_val_if_fail (item != NULL, FALSE);
943
944   GST_DEBUG_OBJECT (nice, "Setting local ICE credentials on "
945       "ICE stream %u ufrag:%s pwd:%s", item->nice_stream_id, ufrag, pwd);
946
947   nice_agent_set_local_credentials (nice->priv->nice_agent,
948       item->nice_stream_id, ufrag, pwd);
949
950   return TRUE;
951 }
952
953 static gboolean
954 gst_webrtc_nice_gather_candidates (GstWebRTCICE * ice,
955     GstWebRTCICEStream * stream)
956 {
957   struct NiceStreamItem *item;
958   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
959
960   item = _find_item (nice, -1, -1, stream);
961   g_return_val_if_fail (item != NULL, FALSE);
962
963   GST_DEBUG_OBJECT (nice, "gather candidates for stream %u",
964       item->nice_stream_id);
965
966   return gst_webrtc_ice_stream_gather_candidates (stream);
967 }
968
969 static void
970 gst_webrtc_nice_set_is_controller (GstWebRTCICE * ice, gboolean controller)
971 {
972   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
973   g_object_set (G_OBJECT (nice->priv->nice_agent), "controlling-mode",
974       controller, NULL);
975 }
976
977 static gboolean
978 gst_webrtc_nice_get_is_controller (GstWebRTCICE * ice)
979 {
980   gboolean ret;
981   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
982   g_object_get (G_OBJECT (nice->priv->nice_agent), "controlling-mode",
983       &ret, NULL);
984   return ret;
985 }
986
987 static void
988 gst_webrtc_nice_set_force_relay (GstWebRTCICE * ice, gboolean force_relay)
989 {
990   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
991   g_object_set (G_OBJECT (nice->priv->nice_agent), "force-relay", force_relay,
992       NULL);
993 }
994
995 static void
996 gst_webrtc_nice_set_on_ice_candidate (GstWebRTCICE * ice,
997     GstWebRTCICEOnCandidateFunc func, gpointer user_data, GDestroyNotify notify)
998 {
999   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1000   if (nice->priv->on_candidate_notify)
1001     nice->priv->on_candidate_notify (nice->priv->on_candidate_data);
1002   nice->priv->on_candidate = NULL;
1003
1004   nice->priv->on_candidate = func;
1005   nice->priv->on_candidate_data = user_data;
1006   nice->priv->on_candidate_notify = notify;
1007 }
1008
1009 static void
1010 gst_webrtc_nice_set_tos (GstWebRTCICE * ice, GstWebRTCICEStream * stream,
1011     guint tos)
1012 {
1013   struct NiceStreamItem *item;
1014   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1015
1016   item = _find_item (nice, -1, -1, stream);
1017   g_return_if_fail (item != NULL);
1018
1019   nice_agent_set_stream_tos (nice->priv->nice_agent, item->nice_stream_id, tos);
1020 }
1021
1022 static const gchar *
1023 _relay_type_to_string (GstUri * turn_server)
1024 {
1025   const gchar *scheme;
1026   const gchar *transport;
1027
1028   if (!turn_server)
1029     return "none";
1030
1031   scheme = gst_uri_get_scheme (turn_server);
1032   transport = gst_uri_get_query_value (turn_server, "transport");
1033
1034   if (g_strcmp0 (scheme, "turns") == 0) {
1035     return "tls";
1036   } else if (g_strcmp0 (scheme, "turn") == 0) {
1037     if (!transport || g_strcmp0 (transport, "udp") == 0)
1038       return "udp";
1039     if (!transport || g_strcmp0 (transport, "tcp") == 0)
1040       return "tcp";
1041   }
1042
1043   return "none";
1044 }
1045
1046 static gchar *
1047 _get_server_url (GstWebRTCNice * ice, NiceCandidate * cand)
1048 {
1049   switch (cand->type) {
1050     case NICE_CANDIDATE_TYPE_RELAYED:{
1051       NiceAddress addr;
1052       gchar ipaddr[NICE_ADDRESS_STRING_LEN];
1053       nice_candidate_relay_address (cand, &addr);
1054       nice_address_to_string (&addr, ipaddr);
1055       return g_strdup (ipaddr);
1056     }
1057     case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:{
1058       NiceAddress addr;
1059       gchar ipaddr[NICE_ADDRESS_STRING_LEN];
1060       if (nice_candidate_stun_server_address (cand, &addr)) {
1061         nice_address_to_string (&addr, ipaddr);
1062         return g_strdup (ipaddr);
1063       } else {
1064         return g_strdup (gst_uri_get_host (ice->priv->stun_server));
1065       }
1066 #ifndef TIZEN_FEATURE_WEBRTC_MODIFICATION
1067       return g_strdup (gst_uri_get_host (ice->priv->stun_server));
1068 #endif
1069     }
1070     default:
1071       return g_strdup ("");
1072   }
1073 }
1074
1075 /* TODO: replace it with nice_candidate_type_to_string()
1076  * when it's ready for use
1077  * https://libnice.freedesktop.org/libnice/NiceCandidate.html#nice-candidate-type-to-string
1078  */
1079 static const gchar *
1080 _candidate_type_to_string (NiceCandidateType type)
1081 {
1082   switch (type) {
1083     case NICE_CANDIDATE_TYPE_HOST:
1084       return "host";
1085     case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
1086       return "srflx";
1087     case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
1088       return "prflx";
1089     case NICE_CANDIDATE_TYPE_RELAYED:
1090       return "relay";
1091     default:
1092       g_assert_not_reached ();
1093       return NULL;
1094   }
1095 }
1096
1097 static void
1098 _populate_candidate_stats (GstWebRTCNice * ice, NiceCandidate * cand,
1099     GstWebRTCICEStream * stream, GstWebRTCICECandidateStats * stats,
1100     gboolean is_local)
1101 {
1102   gchar ipaddr[INET6_ADDRSTRLEN];
1103
1104   g_assert (cand != NULL);
1105
1106   nice_address_to_string (&cand->addr, ipaddr);
1107   stats->port = nice_address_get_port (&cand->addr);
1108   stats->ipaddr = g_strdup (ipaddr);
1109   stats->stream_id = stream->stream_id;
1110   stats->type = _candidate_type_to_string (cand->type);
1111   stats->prio = cand->priority;
1112   stats->proto =
1113       cand->transport == NICE_CANDIDATE_TRANSPORT_UDP ? "udp" : "tcp";
1114   if (is_local) {
1115     if (cand->type == NICE_CANDIDATE_TYPE_RELAYED)
1116       stats->relay_proto = _relay_type_to_string (ice->priv->turn_server);
1117     stats->url = _get_server_url (ice, cand);
1118   }
1119 }
1120
1121 static void
1122 _populate_candidate_list_stats (GstWebRTCNice * ice, GSList * cands,
1123     GstWebRTCICEStream * stream, GPtrArray * result, gboolean is_local)
1124 {
1125   GSList *item;
1126
1127   for (item = cands; item != NULL; item = item->next) {
1128     GstWebRTCICECandidateStats *stats =
1129         g_malloc0 (sizeof (GstWebRTCICECandidateStats));
1130     NiceCandidate *c = item->data;
1131     _populate_candidate_stats (ice, c, stream, stats, is_local);
1132     g_ptr_array_add (result, stats);
1133   }
1134
1135   g_ptr_array_add (result, NULL);
1136 }
1137
1138 static GstWebRTCICECandidateStats **
1139 gst_webrtc_nice_get_local_candidates (GstWebRTCICE * ice,
1140     GstWebRTCICEStream * stream)
1141 {
1142   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1143   GSList *cands = NULL;
1144
1145   /* TODO: Use a g_ptr_array_new_null_terminated once when we depend on GLib 2.74 */
1146   GPtrArray *result = g_ptr_array_new ();
1147
1148   cands = nice_agent_get_local_candidates (nice->priv->nice_agent,
1149       stream->stream_id, NICE_COMPONENT_TYPE_RTP);
1150
1151   _populate_candidate_list_stats (nice, cands, stream, result, TRUE);
1152   g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
1153
1154   return (GstWebRTCICECandidateStats **) g_ptr_array_free (result, FALSE);
1155 }
1156
1157 static GstWebRTCICECandidateStats **
1158 gst_webrtc_nice_get_remote_candidates (GstWebRTCICE * ice,
1159     GstWebRTCICEStream * stream)
1160 {
1161   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1162   GSList *cands = NULL;
1163
1164   /* TODO: Use a g_ptr_array_new_null_terminated once when we depend on GLib 2.74 */
1165   GPtrArray *result = g_ptr_array_new ();
1166
1167   cands = nice_agent_get_remote_candidates (nice->priv->nice_agent,
1168       stream->stream_id, NICE_COMPONENT_TYPE_RTP);
1169
1170   _populate_candidate_list_stats (nice, cands, stream, result, FALSE);
1171   g_slist_free_full (cands, (GDestroyNotify) nice_candidate_free);
1172
1173   return (GstWebRTCICECandidateStats **) g_ptr_array_free (result, FALSE);
1174 }
1175
1176 static gboolean
1177 gst_webrtc_nice_get_selected_pair (GstWebRTCICE * ice,
1178     GstWebRTCICEStream * stream, GstWebRTCICECandidateStats ** local_stats,
1179     GstWebRTCICECandidateStats ** remote_stats)
1180 {
1181   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1182   NiceCandidate *local_cand = NULL;
1183   NiceCandidate *remote_cand = NULL;
1184
1185
1186   if (stream) {
1187     if (nice_agent_get_selected_pair (nice->priv->nice_agent, stream->stream_id,
1188             NICE_COMPONENT_TYPE_RTP, &local_cand, &remote_cand)) {
1189       *local_stats = g_new0 (GstWebRTCICECandidateStats, 1);
1190       _populate_candidate_stats (nice, local_cand, stream, *local_stats, TRUE);
1191
1192       *remote_stats = g_new0 (GstWebRTCICECandidateStats, 1);
1193       _populate_candidate_stats (nice, remote_cand, stream, *remote_stats,
1194           FALSE);
1195
1196       return TRUE;
1197     }
1198   }
1199
1200   return FALSE;
1201 }
1202
1203 static void
1204 _clear_ice_stream (struct NiceStreamItem *item)
1205 {
1206   GstWebRTCNice *ice = NULL;
1207
1208   if (!item)
1209     return;
1210
1211   if (item->stream) {
1212     g_object_get (item->stream, "ice", &ice, NULL);
1213
1214     if (ice != NULL) {
1215       g_signal_handlers_disconnect_by_data (ice->priv->nice_agent,
1216           item->stream);
1217       gst_object_unref (ice);
1218     }
1219     gst_object_unref (item->stream);
1220   }
1221 }
1222
1223 static GstUri *
1224 _validate_turn_server (GstWebRTCNice * ice, const gchar * s)
1225 {
1226   GstUri *uri = gst_uri_from_string_escaped (s);
1227   const gchar *userinfo, *scheme;
1228   GList *keys = NULL, *l;
1229   gchar *user = NULL, *pass = NULL;
1230   gboolean turn_tls = FALSE;
1231   guint port;
1232
1233   GST_DEBUG_OBJECT (ice, "validating turn server, %s", s);
1234
1235   if (!uri) {
1236     GST_ERROR_OBJECT (ice, "Could not parse turn server '%s'", s);
1237     return NULL;
1238   }
1239
1240   scheme = gst_uri_get_scheme (uri);
1241   if (g_strcmp0 (scheme, "turn") == 0) {
1242   } else if (g_strcmp0 (scheme, "turns") == 0) {
1243     turn_tls = TRUE;
1244   } else {
1245     GST_ERROR_OBJECT (ice, "unknown scheme '%s'", scheme);
1246     goto out;
1247   }
1248
1249   keys = gst_uri_get_query_keys (uri);
1250   for (l = keys; l; l = l->next) {
1251     gchar *key = l->data;
1252
1253     if (g_strcmp0 (key, "transport") == 0) {
1254       const gchar *transport = gst_uri_get_query_value (uri, "transport");
1255       if (!transport) {
1256       } else if (g_strcmp0 (transport, "udp") == 0) {
1257       } else if (g_strcmp0 (transport, "tcp") == 0) {
1258       } else {
1259         GST_ERROR_OBJECT (ice, "unknown transport value, '%s'", transport);
1260         goto out;
1261       }
1262     } else {
1263       GST_ERROR_OBJECT (ice, "unknown query key, '%s'", key);
1264       goto out;
1265     }
1266   }
1267
1268   /* TODO: Implement error checking similar to the stun server below */
1269   userinfo = gst_uri_get_userinfo (uri);
1270   _parse_userinfo (userinfo, &user, &pass);
1271   if (!user) {
1272     GST_ERROR_OBJECT (ice, "No username specified in '%s'", s);
1273     goto out;
1274   }
1275   if (!pass) {
1276     GST_ERROR_OBJECT (ice, "No password specified in '%s'", s);
1277     goto out;
1278   }
1279
1280   port = gst_uri_get_port (uri);
1281
1282   if (port == GST_URI_NO_PORT) {
1283     if (turn_tls) {
1284       gst_uri_set_port (uri, 5349);
1285     } else {
1286       gst_uri_set_port (uri, 3478);
1287     }
1288   }
1289
1290   g_list_free (keys);
1291   g_free (user);
1292   g_free (pass);
1293
1294   return uri;
1295
1296 out:
1297   g_list_free (keys);
1298   g_free (user);
1299   g_free (pass);
1300   gst_uri_unref (uri);
1301
1302   return NULL;
1303 }
1304
1305 static void
1306 on_http_proxy_resolved (GstWebRTCICE * ice, GAsyncResult * res,
1307     gpointer user_data)
1308 {
1309   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1310   GstUri *uri = user_data;
1311   GList *addresses;
1312   GError *error = NULL;
1313   const gchar *userinfo;
1314   gchar *user = NULL;
1315   gchar *pass = NULL;
1316   gchar *ip = NULL;
1317   guint port = GST_URI_NO_PORT;
1318
1319   if (!(addresses = resolve_host_finish (nice, res, &error))) {
1320     GST_WARNING_OBJECT (ice, "Failed to resolve http proxy: %s",
1321         error->message);
1322     g_clear_error (&error);
1323     return;
1324   }
1325
1326   /* XXX: only the first IP is used */
1327   ip = g_inet_address_to_string (addresses->data);
1328   g_resolver_free_addresses (addresses);
1329   addresses = NULL;
1330
1331   if (!ip) {
1332     GST_ERROR_OBJECT (ice, "failed to resolve host for proxy");
1333     gst_uri_unref (uri);
1334     return;
1335   }
1336
1337   port = gst_uri_get_port (uri);
1338   if (port == GST_URI_NO_PORT) {
1339     port = HTTP_PROXY_PORT_DEFAULT;
1340     GST_DEBUG_OBJECT (ice, "Proxy server has no port, assuming %u",
1341         HTTP_PROXY_PORT_DEFAULT);
1342   }
1343
1344   userinfo = gst_uri_get_userinfo (uri);
1345   _parse_userinfo (userinfo, &user, &pass);
1346
1347   g_object_set (nice->priv->nice_agent,
1348       "proxy-ip", ip, "proxy-port", port, "proxy-type", NICE_PROXY_TYPE_HTTP,
1349       "proxy-username", user, "proxy-password", pass, NULL);
1350
1351   g_free (ip);
1352   g_free (user);
1353   g_free (pass);
1354 }
1355
1356 static GstUri *
1357 _set_http_proxy (GstWebRTCICE * ice, const gchar * s)
1358 {
1359   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1360   GstUri *uri = gst_uri_from_string_escaped (s);
1361   const gchar *msg =
1362       "must be of the form http://[username:password@]<host>[:<port>]";
1363   const gchar *host = NULL;
1364   const gchar *userinfo;
1365   gchar *user = NULL, *pass = NULL;
1366
1367   GST_DEBUG_OBJECT (ice, "setting http proxy %s", s);
1368
1369   if (!uri) {
1370     GST_ERROR_OBJECT (ice, "Couldn't parse http proxy uri '%s', %s", s, msg);
1371     return NULL;
1372   }
1373
1374   if (g_strcmp0 (gst_uri_get_scheme (uri), "http") != 0) {
1375     GST_ERROR_OBJECT (ice,
1376         "Couldn't parse uri scheme for http proxy server '%s', %s", s, msg);
1377     gst_uri_unref (uri);
1378     return NULL;
1379   }
1380
1381   host = gst_uri_get_host (uri);
1382   if (!host) {
1383     GST_ERROR_OBJECT (ice, "http proxy server '%s' has no host, %s", s, msg);
1384     gst_uri_unref (uri);
1385     return NULL;
1386   }
1387
1388   userinfo = gst_uri_get_userinfo (uri);
1389   _parse_userinfo (userinfo, &user, &pass);
1390   if ((pass && pass[0] != '\0') && (!user || user[0] == '\0')) {
1391     GST_ERROR_OBJECT (ice,
1392         "Password specified without user for http proxy '%s', %s", s, msg);
1393     uri = NULL;
1394     goto out;
1395   }
1396
1397   resolve_host_async (nice, host, (GAsyncReadyCallback) on_http_proxy_resolved,
1398       gst_uri_ref (uri), (GDestroyNotify) gst_uri_unref);
1399
1400 out:
1401   g_free (user);
1402   g_free (pass);
1403
1404   return uri;
1405 }
1406
1407 static void
1408 gst_webrtc_nice_set_stun_server (GstWebRTCICE * ice, const gchar * uri_s)
1409 {
1410   GstUri *uri = gst_uri_from_string_escaped (uri_s);
1411   const gchar *msg = "must be of the form stun://<host>:<port>";
1412   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1413
1414   GST_DEBUG_OBJECT (nice, "setting stun server, %s", uri_s);
1415
1416   if (!uri) {
1417     GST_ERROR_OBJECT (nice, "Couldn't parse stun server '%s', %s", uri_s, msg);
1418     return;
1419   }
1420
1421   if (nice->priv->stun_server)
1422     gst_uri_unref (nice->priv->stun_server);
1423   nice->priv->stun_server = uri;
1424 }
1425
1426 static gchar *
1427 gst_webrtc_nice_get_stun_server (GstWebRTCICE * ice)
1428 {
1429   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1430   if (nice->priv->stun_server)
1431     return gst_uri_to_string (nice->priv->stun_server);
1432   else
1433     return NULL;
1434 }
1435
1436 static void
1437 gst_webrtc_nice_set_turn_server (GstWebRTCICE * ice, const gchar * uri_s)
1438 {
1439   GstUri *uri;
1440   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1441   uri = _validate_turn_server (nice, uri_s);
1442
1443   if (uri) {
1444     if (nice->priv->turn_server)
1445       gst_uri_unref (nice->priv->turn_server);
1446     nice->priv->turn_server = uri;
1447   }
1448 }
1449
1450 static gchar *
1451 gst_webrtc_nice_get_turn_server (GstWebRTCICE * ice)
1452 {
1453   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1454   if (nice->priv->turn_server)
1455     return gst_uri_to_string (nice->priv->turn_server);
1456   else
1457     return NULL;
1458 }
1459
1460 static void
1461 gst_webrtc_nice_set_http_proxy (GstWebRTCICE * ice, const gchar * http_proxy)
1462 {
1463   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1464   GstUri *uri = _set_http_proxy (ice, http_proxy);
1465
1466   if (uri) {
1467     if (nice->priv->http_proxy)
1468       gst_uri_unref (nice->priv->http_proxy);
1469     nice->priv->http_proxy = uri;
1470   }
1471 }
1472
1473 static gchar *
1474 gst_webrtc_nice_get_http_proxy (GstWebRTCICE * ice)
1475 {
1476   GstWebRTCNice *nice = GST_WEBRTC_NICE (ice);
1477
1478   if (nice->priv->http_proxy)
1479     return gst_uri_to_string (nice->priv->http_proxy);
1480   else
1481     return NULL;
1482 }
1483
1484 static void
1485 gst_webrtc_nice_set_property (GObject * object, guint prop_id,
1486     const GValue * value, GParamSpec * pspec)
1487 {
1488   GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1489   GstWebRTCNice *nice = GST_WEBRTC_NICE (object);
1490
1491   switch (prop_id) {
1492     case PROP_ICE_TCP:
1493       g_object_set_property (G_OBJECT (nice->priv->nice_agent),
1494           "ice-tcp", value);
1495       break;
1496     case PROP_ICE_UDP:
1497       g_object_set_property (G_OBJECT (nice->priv->nice_agent),
1498           "ice-udp", value);
1499       break;
1500     case PROP_MIN_RTP_PORT:
1501       ice->min_rtp_port = g_value_get_uint (value);
1502       if (ice->min_rtp_port > ice->max_rtp_port)
1503         g_warning ("Set min-rtp-port to %u which is larger than"
1504             " max-rtp-port %u", ice->min_rtp_port, ice->max_rtp_port);
1505       break;
1506     case PROP_MAX_RTP_PORT:
1507       ice->max_rtp_port = g_value_get_uint (value);
1508       if (ice->min_rtp_port > ice->max_rtp_port)
1509         g_warning ("Set max-rtp-port to %u which is smaller than"
1510             " min-rtp-port %u", ice->max_rtp_port, ice->min_rtp_port);
1511       break;
1512     default:
1513       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1514       break;
1515   }
1516 }
1517
1518 static void
1519 gst_webrtc_nice_get_property (GObject * object, guint prop_id,
1520     GValue * value, GParamSpec * pspec)
1521 {
1522   GstWebRTCICE *ice = GST_WEBRTC_ICE (object);
1523   GstWebRTCNice *nice = GST_WEBRTC_NICE (object);
1524
1525   switch (prop_id) {
1526     case PROP_AGENT:
1527       g_value_set_object (value, nice->priv->nice_agent);
1528       break;
1529     case PROP_ICE_TCP:
1530       g_object_get_property (G_OBJECT (nice->priv->nice_agent),
1531           "ice-tcp", value);
1532       break;
1533     case PROP_ICE_UDP:
1534       g_object_get_property (G_OBJECT (nice->priv->nice_agent),
1535           "ice-udp", value);
1536       break;
1537     case PROP_MIN_RTP_PORT:
1538       g_value_set_uint (value, ice->min_rtp_port);
1539       break;
1540     case PROP_MAX_RTP_PORT:
1541       g_value_set_uint (value, ice->max_rtp_port);
1542       break;
1543     default:
1544       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1545       break;
1546   }
1547 }
1548
1549 static void
1550 gst_webrtc_nice_finalize (GObject * object)
1551 {
1552   GstWebRTCNice *ice = GST_WEBRTC_NICE (object);
1553
1554   g_signal_handlers_disconnect_by_data (ice->priv->nice_agent, ice);
1555
1556   _stop_thread (ice);
1557
1558   if (ice->priv->on_candidate_notify)
1559     ice->priv->on_candidate_notify (ice->priv->on_candidate_data);
1560   ice->priv->on_candidate = NULL;
1561   ice->priv->on_candidate_notify = NULL;
1562
1563   if (ice->priv->turn_server)
1564     gst_uri_unref (ice->priv->turn_server);
1565   if (ice->priv->stun_server)
1566     gst_uri_unref (ice->priv->stun_server);
1567   if (ice->priv->http_proxy)
1568     gst_uri_unref (ice->priv->http_proxy);
1569
1570   g_mutex_clear (&ice->priv->lock);
1571   g_cond_clear (&ice->priv->cond);
1572
1573   g_array_free (ice->priv->nice_stream_map, TRUE);
1574
1575   g_object_unref (ice->priv->nice_agent);
1576
1577   g_hash_table_unref (ice->priv->turn_servers);
1578
1579   G_OBJECT_CLASS (parent_class)->finalize (object);
1580 }
1581
1582 static void
1583 gst_webrtc_nice_constructed (GObject * object)
1584 {
1585   GstWebRTCNice *ice = GST_WEBRTC_NICE (object);
1586   NiceAgentOption options = 0;
1587
1588   _start_thread (ice);
1589
1590   options |= NICE_AGENT_OPTION_ICE_TRICKLE;
1591   options |= NICE_AGENT_OPTION_REGULAR_NOMINATION;
1592
1593   ice->priv->nice_agent = nice_agent_new_full (ice->priv->main_context,
1594       NICE_COMPATIBILITY_RFC5245, options);
1595   g_signal_connect (ice->priv->nice_agent, "new-candidate-full",
1596       G_CALLBACK (_on_new_candidate), ice);
1597
1598   G_OBJECT_CLASS (parent_class)->constructed (object);
1599 }
1600
1601 static void
1602 gst_webrtc_nice_class_init (GstWebRTCNiceClass * klass)
1603 {
1604   GstWebRTCICEClass *gst_webrtc_ice_class = GST_WEBRTC_ICE_CLASS (klass);
1605   GObjectClass *gobject_class = (GObjectClass *) klass;
1606
1607   // override virtual functions
1608   gst_webrtc_ice_class->add_candidate = gst_webrtc_nice_add_candidate;
1609   gst_webrtc_ice_class->add_stream = gst_webrtc_nice_add_stream;
1610   gst_webrtc_ice_class->add_turn_server = gst_webrtc_nice_add_turn_server;
1611   gst_webrtc_ice_class->find_transport = gst_webrtc_nice_find_transport;
1612   gst_webrtc_ice_class->gather_candidates = gst_webrtc_nice_gather_candidates;
1613   gst_webrtc_ice_class->get_is_controller = gst_webrtc_nice_get_is_controller;
1614   gst_webrtc_ice_class->get_stun_server = gst_webrtc_nice_get_stun_server;
1615   gst_webrtc_ice_class->get_turn_server = gst_webrtc_nice_get_turn_server;
1616   gst_webrtc_ice_class->get_http_proxy = gst_webrtc_nice_get_http_proxy;
1617   gst_webrtc_ice_class->set_force_relay = gst_webrtc_nice_set_force_relay;
1618   gst_webrtc_ice_class->set_is_controller = gst_webrtc_nice_set_is_controller;
1619   gst_webrtc_ice_class->set_local_credentials =
1620       gst_webrtc_nice_set_local_credentials;
1621   gst_webrtc_ice_class->set_remote_credentials =
1622       gst_webrtc_nice_set_remote_credentials;
1623   gst_webrtc_ice_class->set_stun_server = gst_webrtc_nice_set_stun_server;
1624   gst_webrtc_ice_class->set_tos = gst_webrtc_nice_set_tos;
1625   gst_webrtc_ice_class->set_turn_server = gst_webrtc_nice_set_turn_server;
1626   gst_webrtc_ice_class->set_http_proxy = gst_webrtc_nice_set_http_proxy;
1627   gst_webrtc_ice_class->set_on_ice_candidate =
1628       gst_webrtc_nice_set_on_ice_candidate;
1629   gst_webrtc_ice_class->get_local_candidates =
1630       gst_webrtc_nice_get_local_candidates;
1631   gst_webrtc_ice_class->get_remote_candidates =
1632       gst_webrtc_nice_get_remote_candidates;
1633   gst_webrtc_ice_class->get_selected_pair = gst_webrtc_nice_get_selected_pair;
1634
1635   gobject_class->constructed = gst_webrtc_nice_constructed;
1636   gobject_class->get_property = gst_webrtc_nice_get_property;
1637   gobject_class->set_property = gst_webrtc_nice_set_property;
1638   gobject_class->finalize = gst_webrtc_nice_finalize;
1639
1640   g_object_class_install_property (gobject_class,
1641       PROP_AGENT,
1642       g_param_spec_object ("agent", "ICE agent",
1643           "ICE agent in use by this object. WARNING! Accessing this property "
1644           "may have disastrous consequences for the operation of webrtcbin. "
1645           "Other ICE implementations may not have the same interface.",
1646           NICE_TYPE_AGENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1647
1648   g_object_class_install_property (gobject_class,
1649       PROP_ICE_TCP,
1650       g_param_spec_boolean ("ice-tcp", "ICE TCP",
1651           "Whether the agent should use ICE-TCP when gathering candidates",
1652           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1653
1654   g_object_class_install_property (gobject_class,
1655       PROP_ICE_UDP,
1656       g_param_spec_boolean ("ice-udp", "ICE UDP",
1657           "Whether the agent should use ICE-UDP when gathering candidates",
1658           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1659
1660   g_signal_override_class_handler ("add-local-ip-address",
1661       G_TYPE_FROM_CLASS (klass),
1662       G_CALLBACK (gst_webrtc_nice_add_local_ip_address));
1663 }
1664
1665 static void
1666 gst_webrtc_nice_init (GstWebRTCNice * ice)
1667 {
1668   ice->priv = gst_webrtc_nice_get_instance_private (ice);
1669
1670   g_mutex_init (&ice->priv->lock);
1671   g_cond_init (&ice->priv->cond);
1672
1673   ice->priv->turn_servers =
1674       g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1675       (GDestroyNotify) gst_uri_unref);
1676
1677   ice->priv->nice_stream_map =
1678       g_array_new (FALSE, TRUE, sizeof (struct NiceStreamItem));
1679   g_array_set_clear_func (ice->priv->nice_stream_map,
1680       (GDestroyNotify) _clear_ice_stream);
1681 #ifdef TIZEN_FEATURE_WEBRTC_MODIFICATION
1682   nice_debug_enable (TRUE);
1683 #endif
1684 }
1685
1686 GstWebRTCNice *
1687 gst_webrtc_nice_new (const gchar * name)
1688 {
1689   return g_object_new (GST_TYPE_WEBRTC_NICE, "name", name, NULL);
1690 }