Imported Upstream version 0.1.17
[platform/upstream/libnice.git] / examples / sdp-example.c
1 /*
2  * Copyright 2013 University of Chicago
3  *  Contact: Bryce Allen
4  * Copyright 2013 Collabora Ltd.
5  *  Contact: Youness Alaoui
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * Alternatively, the contents of this file may be used under the terms of the
18  * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
19  * case the provisions of LGPL are applicable instead of those above. If you
20  * wish to allow use of your version of this file only under the terms of the
21  * LGPL and not to allow others to use your version of this file under the
22  * MPL, indicate your decision by deleting the provisions above and replace
23  * them with the notice and other provisions required by the LGPL. If you do
24  * not delete the provisions above, a recipient may use your version of this
25  * file under either the MPL or the LGPL.
26  */
27
28 /*
29  * Example using libnice to negotiate a UDP connection between two clients,
30  * possibly on the same network or behind different NATs and/or stateful
31  * firewalls.
32  *
33  * Build:
34  *   gcc -o sdp-example sdp-example.c `pkg-config --cflags --libs nice`
35  *
36  * Run two clients, one controlling and one controlled:
37  *   sdp-example 0 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
38  *   sdp-example 1 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
39  */
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <ctype.h>
44
45 #include <agent.h>
46
47 #include <gio/gnetworking.h>
48
49 static GMainLoop *gloop;
50 static gchar *stun_addr = NULL;
51 static guint stun_port;
52 static gboolean controlling;
53 static gboolean exit_thread, candidate_gathering_done, negotiation_done;
54 static GMutex gather_mutex, negotiate_mutex;
55 static GCond gather_cond, negotiate_cond;
56
57 static const gchar *state_name[] = {"disconnected", "gathering", "connecting",
58                                     "connected", "ready", "failed"};
59
60 static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id,
61     gpointer data);
62 static void cb_component_state_changed(NiceAgent *agent, guint stream_id,
63     guint component_id, guint state,
64     gpointer data);
65 static void cb_nice_recv(NiceAgent *agent, guint stream_id, guint component_id,
66     guint len, gchar *buf, gpointer data);
67
68 static void * example_thread(void *data);
69
70 int
71 main(int argc, char *argv[])
72 {
73   GThread *gexamplethread;
74
75   // Parse arguments
76   if (argc > 4 || argc < 2 || argv[1][1] != '\0') {
77     fprintf(stderr, "Usage: %s 0|1 stun_addr [stun_port]\n", argv[0]);
78     return EXIT_FAILURE;
79   }
80   controlling = argv[1][0] - '0';
81   if (controlling != 0 && controlling != 1) {
82     fprintf(stderr, "Usage: %s 0|1 stun_addr [stun_port]\n", argv[0]);
83     return EXIT_FAILURE;
84   }
85
86   if (argc > 2) {
87     stun_addr = argv[2];
88     if (argc > 3)
89       stun_port = atoi(argv[3]);
90     else
91       stun_port = 3478;
92
93     g_debug("Using stun server '[%s]:%u'\n", stun_addr, stun_port);
94   }
95
96   g_networking_init();
97
98   gloop = g_main_loop_new(NULL, FALSE);
99
100   // Run the mainloop and the example thread
101   exit_thread = FALSE;
102   gexamplethread = g_thread_new("example thread", &example_thread, NULL);
103   g_main_loop_run (gloop);
104   exit_thread = TRUE;
105
106   g_thread_join (gexamplethread);
107   g_main_loop_unref(gloop);
108
109   return EXIT_SUCCESS;
110 }
111
112 static void *
113 example_thread(void *data)
114 {
115   NiceAgent *agent;
116   GIOChannel* io_stdin;
117   guint stream_id;
118   gchar *line = NULL;
119   gchar *sdp, *sdp64;
120
121 #ifdef G_OS_WIN32
122   io_stdin = g_io_channel_win32_new_fd(_fileno(stdin));
123 #else
124   io_stdin = g_io_channel_unix_new(fileno(stdin));
125 #endif
126   g_io_channel_set_flags(io_stdin, G_IO_FLAG_NONBLOCK, NULL);
127
128   // Create the nice agent
129   agent = nice_agent_new(g_main_loop_get_context (gloop),
130       NICE_COMPATIBILITY_RFC5245);
131   if (agent == NULL)
132     g_error("Failed to create agent");
133
134   // Set the STUN settings and controlling mode
135   if (stun_addr) {
136     g_object_set(agent, "stun-server", stun_addr, NULL);
137     g_object_set(agent, "stun-server-port", stun_port, NULL);
138   }
139   g_object_set(agent, "controlling-mode", controlling, NULL);
140
141   // Connect to the signals
142   g_signal_connect(agent, "candidate-gathering-done",
143       G_CALLBACK(cb_candidate_gathering_done), NULL);
144   g_signal_connect(agent, "component-state-changed",
145       G_CALLBACK(cb_component_state_changed), NULL);
146
147   // Create a new stream with one component
148   stream_id = nice_agent_add_stream(agent, 1);
149   if (stream_id == 0)
150     g_error("Failed to add stream");
151   nice_agent_set_stream_name (agent, stream_id, "text");
152
153   // Attach to the component to receive the data
154   // Without this call, candidates cannot be gathered
155   nice_agent_attach_recv(agent, stream_id, 1,
156       g_main_loop_get_context (gloop), cb_nice_recv, NULL);
157
158   // Start gathering local candidates
159   if (!nice_agent_gather_candidates(agent, stream_id))
160     g_error("Failed to start candidate gathering");
161
162   g_debug("waiting for candidate-gathering-done signal...");
163
164   g_mutex_lock(&gather_mutex);
165   while (!exit_thread && !candidate_gathering_done)
166     g_cond_wait(&gather_cond, &gather_mutex);
167   g_mutex_unlock(&gather_mutex);
168   if (exit_thread)
169     goto end;
170
171   // Candidate gathering is done. Send our local candidates on stdout
172   sdp = nice_agent_generate_local_sdp (agent);
173   printf("Generated SDP from agent :\n%s\n\n", sdp);
174   printf("Copy the following line to remote client:\n");
175   sdp64 = g_base64_encode ((const guchar *)sdp, strlen (sdp));
176   printf("\n  %s\n", sdp64);
177   g_free (sdp);
178   g_free (sdp64);
179
180   // Listen on stdin for the remote candidate list
181   printf("Enter remote data (single line, no wrapping):\n");
182   printf("> ");
183   fflush (stdout);
184   while (!exit_thread) {
185     GIOStatus s = g_io_channel_read_line (io_stdin, &line, NULL, NULL, NULL);
186     if (s == G_IO_STATUS_NORMAL) {
187       gsize sdp_len;
188
189       sdp = (gchar *) g_base64_decode (line, &sdp_len);
190       // Parse remote candidate list and set it on the agent
191       if (sdp && nice_agent_parse_remote_sdp (agent, sdp) > 0) {
192         g_free (sdp);
193         g_free (line);
194         break;
195       } else {
196         fprintf(stderr, "ERROR: failed to parse remote data\n");
197         printf("Enter remote data (single line, no wrapping):\n");
198         printf("> ");
199         fflush (stdout);
200       }
201       g_free (sdp);
202       g_free (line);
203     } else if (s == G_IO_STATUS_AGAIN) {
204       g_usleep (100000);
205     }
206   }
207
208   g_debug("waiting for state READY or FAILED signal...");
209   g_mutex_lock(&negotiate_mutex);
210   while (!exit_thread && !negotiation_done)
211     g_cond_wait(&negotiate_cond, &negotiate_mutex);
212   g_mutex_unlock(&negotiate_mutex);
213   if (exit_thread)
214     goto end;
215
216   // Listen to stdin and send data written to it
217   printf("\nSend lines to remote (Ctrl-D to quit):\n");
218   printf("> ");
219   fflush (stdout);
220   while (!exit_thread) {
221     GIOStatus s = g_io_channel_read_line (io_stdin, &line, NULL, NULL, NULL);
222
223     if (s == G_IO_STATUS_NORMAL) {
224       nice_agent_send(agent, stream_id, 1, strlen(line), line);
225       g_free (line);
226       printf("> ");
227       fflush (stdout);
228     } else if (s == G_IO_STATUS_AGAIN) {
229       g_usleep (100000);
230     } else {
231       // Ctrl-D was pressed.
232       nice_agent_send(agent, stream_id, 1, 1, "\0");
233       break;
234     }
235   }
236
237 end:
238   g_object_unref(agent);
239   g_io_channel_unref (io_stdin);
240   g_main_loop_quit (gloop);
241
242   return NULL;
243 }
244
245 static void
246 cb_candidate_gathering_done(NiceAgent *agent, guint stream_id,
247     gpointer data)
248 {
249   g_debug("SIGNAL candidate gathering done\n");
250
251   g_mutex_lock(&gather_mutex);
252   candidate_gathering_done = TRUE;
253   g_cond_signal(&gather_cond);
254   g_mutex_unlock(&gather_mutex);
255 }
256
257 static void
258 cb_component_state_changed(NiceAgent *agent, guint stream_id,
259     guint component_id, guint state,
260     gpointer data)
261 {
262   g_debug("SIGNAL: state changed %d %d %s[%d]\n",
263       stream_id, component_id, state_name[state], state);
264
265   if (state == NICE_COMPONENT_STATE_READY) {
266     g_mutex_lock(&negotiate_mutex);
267     negotiation_done = TRUE;
268     g_cond_signal(&negotiate_cond);
269     g_mutex_unlock(&negotiate_mutex);
270   } else if (state == NICE_COMPONENT_STATE_FAILED) {
271     g_main_loop_quit (gloop);
272   }
273 }
274
275 static void
276 cb_nice_recv(NiceAgent *agent, guint stream_id, guint component_id,
277     guint len, gchar *buf, gpointer data)
278 {
279   if (len == 1 && buf[0] == '\0')
280     g_main_loop_quit (gloop);
281
282   printf("%.*s", len, buf);
283   fflush(stdout);
284 }