1 /* Farstream ad-hoc test for simple calls.
2 *
3 * Copyright (C) 2008 Collabora, Nokia
4 * @author: Olivier Crete <olivier.crete@collabora.co.uk>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*
22 * WARNING:
23 *
24 * Do not use this as an example of a proper use of farstream, it assumes that
25 * both ends have the EXACT same list of codec installed in the EXACT same order
26 */
27
28
29 #include <stdlib.h>
30
31 #include <glib.h>
32 #include <gst/gst.h>
33 #include <farstream/fs-conference.h>
34
35 #define DEFAULT_AUDIOSRC "audiotestsrc is-live=1"
36 #define DEFAULT_AUDIOSINK "audioconvert ! audioresample ! audioconvert ! alsasink"
37
38 typedef struct _TestSession
39 {
40 FsSession *session;
41 FsStream *stream;
42 } TestSession;
43
44
45 static void
print_error(GError * error)46 print_error (GError *error)
47 {
48 if (error)
49 {
50 g_error ("Error: %s:%d : %s", g_quark_to_string (error->domain),
51 error->code, error->message);
52 }
53 }
54
55 static void
src_pad_added_cb(FsStream * stream,GstPad * pad,FsCodec * codec,gpointer user_data)56 src_pad_added_cb (FsStream *stream, GstPad *pad, FsCodec *codec,
57 gpointer user_data)
58 {
59 GstElement *pipeline = GST_ELEMENT_CAST (user_data);
60 GstElement *sink = NULL;
61 GError *error = NULL;
62 GstPad *pad2;
63
64 g_print ("Adding receive pipeline\n");
65
66 if (g_getenv ("AUDIOSINK"))
67 sink = gst_parse_bin_from_description (g_getenv ("AUDIOSINK"), TRUE,
68 &error);
69 else
70 sink = gst_parse_bin_from_description (DEFAULT_AUDIOSINK, TRUE,
71 &error);
72 print_error (error);
73 g_assert (sink);
74
75 g_assert (gst_bin_add (GST_BIN (pipeline), sink));
76
77
78 pad2 = gst_element_get_static_pad (sink, "sink");
79 g_assert (pad2);
80
81 g_assert (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (pad, pad2)));
82
83 g_assert (gst_element_set_state (sink, GST_STATE_PLAYING) !=
84 GST_STATE_CHANGE_FAILURE);
85
86 gst_object_unref (pad2);
87 }
88
89 static TestSession*
add_audio_session(GstElement * pipeline,FsConference * conf,guint id,FsParticipant * part,guint localport,const gchar * remoteip,guint remoteport)90 add_audio_session (GstElement *pipeline, FsConference *conf, guint id,
91 FsParticipant *part, guint localport, const gchar *remoteip,
92 guint remoteport)
93 {
94 TestSession *ses = g_slice_new0 (TestSession);
95 GError *error = NULL;
96 GstPad *pad = NULL, *pad2 = NULL;
97 GstElement *src = NULL;
98 GList *cands = NULL;
99 GList *codecs = NULL;
100 GParameter param = {0};
101 gboolean res;
102
103 ses->session = fs_conference_new_session (conf, FS_MEDIA_TYPE_AUDIO, &error);
104 print_error (error);
105 g_assert (ses->session);
106
107 g_object_get (ses->session, "sink-pad", &pad, NULL);
108
109 if (g_getenv ("AUDIOSRC"))
110 src = gst_parse_bin_from_description (g_getenv ("AUDIOSRC"), TRUE,
111 &error);
112 else
113 src = gst_parse_bin_from_description (DEFAULT_AUDIOSRC, TRUE,
114 &error);
115 print_error (error);
116 g_assert (src);
117
118 g_assert (gst_bin_add (GST_BIN (pipeline), src));
119
120 pad2 = gst_element_get_static_pad (src, "src");
121 g_assert (pad2);
122
123 g_assert (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (pad2, pad)));
124
125 gst_object_unref (pad2);
126 gst_object_unref (pad);
127
128
129 ses->stream = fs_session_new_stream (ses->session, part, FS_DIRECTION_BOTH,
130 &error);
131 print_error (error);
132 g_assert (ses->stream);
133
134 cands = g_list_prepend (NULL, fs_candidate_new ("", FS_COMPONENT_RTP,
135 FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, NULL, localport));
136
137 param.name = "preferred-local-candidates";
138 g_value_init (¶m.value, FS_TYPE_CANDIDATE_LIST);
139 g_value_take_boxed (¶m.value, cands);
140
141 fs_stream_set_transmitter (ses->stream, "rawudp", ¶m, 1, &error);
142 print_error (error);
143 g_assert (ses->stream);
144
145 g_value_unset (¶m.value);
146
147 g_signal_connect (ses->stream, "src-pad-added",
148 G_CALLBACK (src_pad_added_cb), pipeline);
149
150 cands = g_list_prepend (NULL, fs_candidate_new ("", FS_COMPONENT_RTP,
151 FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, remoteip,
152 remoteport));
153
154 res = fs_stream_force_remote_candidates (ses->stream, cands, &error);
155 print_error (error);
156 g_assert (res);
157
158 fs_candidate_list_destroy (cands);
159
160 codecs = g_list_prepend (NULL,
161 fs_codec_new (FS_CODEC_ID_ANY, "PCMA", FS_MEDIA_TYPE_AUDIO, 0));
162 codecs = g_list_prepend (codecs,
163 fs_codec_new (FS_CODEC_ID_ANY, "PCMU", FS_MEDIA_TYPE_AUDIO, 0));
164
165 fs_session_set_codec_preferences (ses->session, codecs, &error);
166 print_error (error);
167 fs_codec_list_destroy (codecs);
168
169
170 g_object_get (ses->session, "codecs-without-config", &codecs, NULL);
171 res = fs_stream_set_remote_codecs (ses->stream, codecs, &error);
172 print_error (error);
173 g_assert (res);
174
175 return ses;
176 }
177
178 static gboolean
async_bus_cb(GstBus * bus,GstMessage * message,gpointer user_data)179 async_bus_cb (GstBus *bus, GstMessage *message, gpointer user_data)
180 {
181 switch (GST_MESSAGE_TYPE(message))
182 {
183 case GST_MESSAGE_ERROR:
184 {
185 GError *error = NULL;
186 gchar *debug_str = NULL;
187
188 gst_message_parse_error (message, &error, &debug_str);
189 g_error ("Got gst message: %s %s", error->message, debug_str);
190 }
191 break;
192 case GST_MESSAGE_WARNING:
193 {
194 GError *error = NULL;
195 gchar *debug_str = NULL;
196
197 gst_message_parse_warning (message, &error, &debug_str);
198 g_warning ("Got gst message: %s %s", error->message, debug_str);
199 }
200 break;
201 case GST_MESSAGE_ELEMENT:
202 {
203 const GstStructure *s = gst_message_get_structure (message);
204
205 if (gst_structure_has_name (s, "farstream-error"))
206 {
207 gint error;
208 const gchar *error_msg = gst_structure_get_string (s, "error-msg");
209
210 g_assert (gst_structure_get_enum (s, "error-no", FS_TYPE_ERROR,
211 &error));
212
213 if (FS_ERROR_IS_FATAL (error))
214 g_error ("Farstream fatal error: %d %s", error, error_msg);
215 else
216 g_warning ("Farstream non-fatal error: %d %s", error, error_msg);
217 }
218 else if (gst_structure_has_name (s, "farstream-new-local-candidate"))
219 {
220 const GValue *val = gst_structure_get_value (s, "candidate");
221 FsCandidate *cand = NULL;
222
223 g_assert (val);
224 cand = g_value_get_boxed (val);
225
226 g_print ("New candidate: %s %d\n", cand->ip, cand->port);
227 }
228 else if (gst_structure_has_name (s,
229 "farstream-local-candidates-prepared"))
230 {
231 g_print ("Local candidates prepared\n");
232 }
233 else if (gst_structure_has_name (s, "farstream-recv-codecs-changed"))
234 {
235 const GValue *val = gst_structure_get_value (s, "codecs");
236 GList *codecs = NULL;
237
238 g_assert (val);
239 codecs = g_value_get_boxed (val);
240
241 g_print ("Recv codecs changed:\n");
242 for (; codecs; codecs = g_list_next (codecs))
243 {
244 FsCodec *codec = codecs->data;
245 gchar *tmp = fs_codec_to_string (codec);
246 g_print ("%s\n", tmp);
247 g_free (tmp);
248 }
249 }
250 else if (gst_structure_has_name (s, "farstream-send-codec-changed"))
251 {
252 const GValue *val = gst_structure_get_value (s, "codec");
253 FsCodec *codec = NULL;
254 gchar *tmp;
255 g_assert (val);
256 codec = g_value_get_boxed (val);
257 tmp = fs_codec_to_string (codec);
258
259 g_print ("Send codec changed: %s\n", tmp);
260 g_free (tmp);
261 }
262 }
263 break;
264 default:
265 break;
266 }
267
268 return TRUE;
269 }
270
main(int argc,char ** argv)271 int main (int argc, char **argv)
272 {
273 GMainLoop *loop = NULL;
274 GstElement *pipeline = NULL;
275 GstBus *bus = NULL;
276 const gchar *remoteip;
277 guint localport = 0;
278 guint remoteport = 0;
279 GstElement *conf = NULL;
280 FsParticipant *part = NULL;
281 GError *error = NULL;
282
283 gst_init (&argc, &argv);
284
285 if (argc != 4)
286 {
287 g_print ("Usage: %s <local port> <remoteip> <remoteport>\n",
288 argv[0]);
289 return 1;
290 }
291
292 localport = atoi (argv[1]);
293 remoteip = argv[2];
294 remoteport = atoi (argv[3]);
295
296 if (!localport || !remoteip || !remoteport)
297 {
298 g_print ("Usage: %s <local port> <remoteip> <remoteport>\n",
299 argv[0]);
300 return 2;
301 }
302
303 loop = g_main_loop_new (NULL, FALSE);
304
305 pipeline = gst_pipeline_new (NULL);
306
307 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
308 gst_bus_add_watch (bus, async_bus_cb, pipeline);
309 gst_object_unref (bus);
310
311 conf = gst_element_factory_make ("fsrtpconference", NULL);
312 g_assert (conf);
313
314 part = fs_conference_new_participant (FS_CONFERENCE (conf), &error);
315 print_error (error);
316 g_assert (part);
317
318 g_assert (gst_bin_add (GST_BIN (pipeline), conf));
319
320
321 add_audio_session (pipeline, FS_CONFERENCE (conf), 1, part, localport,
322 remoteip, remoteport);
323
324
325 g_assert (gst_element_set_state (pipeline, GST_STATE_PLAYING) !=
326 GST_STATE_CHANGE_FAILURE);
327
328 g_main_loop_run (loop);
329
330 g_assert (gst_element_set_state (pipeline, GST_STATE_NULL) !=
331 GST_STATE_CHANGE_FAILURE);
332
333 g_object_unref (part);
334
335 gst_object_unref (pipeline);
336 g_main_loop_unref (loop);
337
338 return 0;
339 }
340