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 (&param.value, FS_TYPE_CANDIDATE_LIST);
139   g_value_take_boxed (&param.value, cands);
140 
141   fs_stream_set_transmitter (ses->stream, "rawudp", &param, 1, &error);
142   print_error (error);
143   g_assert (ses->stream);
144 
145   g_value_unset (&param.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