/* Farstream ad-hoc test for simple calls. * * Copyright (C) 2008 Collabora, Nokia * @author: Olivier Crete * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * WARNING: * * Do not use this as an example of a proper use of farstream, it assumes that * both ends have the EXACT same list of codec installed in the EXACT same order */ #include #include #include #include #define DEFAULT_AUDIOSRC "audiotestsrc is-live=1" #define DEFAULT_AUDIOSINK "audioconvert ! audioresample ! audioconvert ! alsasink" typedef struct _TestSession { FsSession *session; FsStream *stream; } TestSession; static void print_error (GError *error) { if (error) { g_error ("Error: %s:%d : %s", g_quark_to_string (error->domain), error->code, error->message); } } static void src_pad_added_cb (FsStream *stream, GstPad *pad, FsCodec *codec, gpointer user_data) { GstElement *pipeline = GST_ELEMENT_CAST (user_data); GstElement *sink = NULL; GError *error = NULL; GstPad *pad2; g_print ("Adding receive pipeline\n"); if (g_getenv ("AUDIOSINK")) sink = gst_parse_bin_from_description (g_getenv ("AUDIOSINK"), TRUE, &error); else sink = gst_parse_bin_from_description (DEFAULT_AUDIOSINK, TRUE, &error); print_error (error); g_assert (sink); g_assert (gst_bin_add (GST_BIN (pipeline), sink)); pad2 = gst_element_get_static_pad (sink, "sink"); g_assert (pad2); g_assert (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (pad, pad2))); g_assert (gst_element_set_state (sink, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE); gst_object_unref (pad2); } static TestSession* add_audio_session (GstElement *pipeline, FsConference *conf, guint id, FsParticipant *part, guint localport, const gchar *remoteip, guint remoteport) { TestSession *ses = g_slice_new0 (TestSession); GError *error = NULL; GstPad *pad = NULL, *pad2 = NULL; GstElement *src = NULL; GList *cands = NULL; GList *codecs = NULL; GParameter param = {0}; gboolean res; ses->session = fs_conference_new_session (conf, FS_MEDIA_TYPE_AUDIO, &error); print_error (error); g_assert (ses->session); g_object_get (ses->session, "sink-pad", &pad, NULL); if (g_getenv ("AUDIOSRC")) src = gst_parse_bin_from_description (g_getenv ("AUDIOSRC"), TRUE, &error); else src = gst_parse_bin_from_description (DEFAULT_AUDIOSRC, TRUE, &error); print_error (error); g_assert (src); g_assert (gst_bin_add (GST_BIN (pipeline), src)); pad2 = gst_element_get_static_pad (src, "src"); g_assert (pad2); g_assert (GST_PAD_LINK_SUCCESSFUL (gst_pad_link (pad2, pad))); gst_object_unref (pad2); gst_object_unref (pad); ses->stream = fs_session_new_stream (ses->session, part, FS_DIRECTION_BOTH, &error); print_error (error); g_assert (ses->stream); cands = g_list_prepend (NULL, fs_candidate_new ("", FS_COMPONENT_RTP, FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, NULL, localport)); param.name = "preferred-local-candidates"; g_value_init (¶m.value, FS_TYPE_CANDIDATE_LIST); g_value_take_boxed (¶m.value, cands); fs_stream_set_transmitter (ses->stream, "rawudp", ¶m, 1, &error); print_error (error); g_assert (ses->stream); g_value_unset (¶m.value); g_signal_connect (ses->stream, "src-pad-added", G_CALLBACK (src_pad_added_cb), pipeline); cands = g_list_prepend (NULL, fs_candidate_new ("", FS_COMPONENT_RTP, FS_CANDIDATE_TYPE_HOST, FS_NETWORK_PROTOCOL_UDP, remoteip, remoteport)); res = fs_stream_force_remote_candidates (ses->stream, cands, &error); print_error (error); g_assert (res); fs_candidate_list_destroy (cands); codecs = g_list_prepend (NULL, fs_codec_new (FS_CODEC_ID_ANY, "PCMA", FS_MEDIA_TYPE_AUDIO, 0)); codecs = g_list_prepend (codecs, fs_codec_new (FS_CODEC_ID_ANY, "PCMU", FS_MEDIA_TYPE_AUDIO, 0)); fs_session_set_codec_preferences (ses->session, codecs, &error); print_error (error); fs_codec_list_destroy (codecs); g_object_get (ses->session, "codecs-without-config", &codecs, NULL); res = fs_stream_set_remote_codecs (ses->stream, codecs, &error); print_error (error); g_assert (res); return ses; } static gboolean async_bus_cb (GstBus *bus, GstMessage *message, gpointer user_data) { switch (GST_MESSAGE_TYPE(message)) { case GST_MESSAGE_ERROR: { GError *error = NULL; gchar *debug_str = NULL; gst_message_parse_error (message, &error, &debug_str); g_error ("Got gst message: %s %s", error->message, debug_str); } break; case GST_MESSAGE_WARNING: { GError *error = NULL; gchar *debug_str = NULL; gst_message_parse_warning (message, &error, &debug_str); g_warning ("Got gst message: %s %s", error->message, debug_str); } break; case GST_MESSAGE_ELEMENT: { const GstStructure *s = gst_message_get_structure (message); if (gst_structure_has_name (s, "farstream-error")) { gint error; const gchar *error_msg = gst_structure_get_string (s, "error-msg"); g_assert (gst_structure_get_enum (s, "error-no", FS_TYPE_ERROR, &error)); if (FS_ERROR_IS_FATAL (error)) g_error ("Farstream fatal error: %d %s", error, error_msg); else g_warning ("Farstream non-fatal error: %d %s", error, error_msg); } else if (gst_structure_has_name (s, "farstream-new-local-candidate")) { const GValue *val = gst_structure_get_value (s, "candidate"); FsCandidate *cand = NULL; g_assert (val); cand = g_value_get_boxed (val); g_print ("New candidate: %s %d\n", cand->ip, cand->port); } else if (gst_structure_has_name (s, "farstream-local-candidates-prepared")) { g_print ("Local candidates prepared\n"); } else if (gst_structure_has_name (s, "farstream-recv-codecs-changed")) { const GValue *val = gst_structure_get_value (s, "codecs"); GList *codecs = NULL; g_assert (val); codecs = g_value_get_boxed (val); g_print ("Recv codecs changed:\n"); for (; codecs; codecs = g_list_next (codecs)) { FsCodec *codec = codecs->data; gchar *tmp = fs_codec_to_string (codec); g_print ("%s\n", tmp); g_free (tmp); } } else if (gst_structure_has_name (s, "farstream-send-codec-changed")) { const GValue *val = gst_structure_get_value (s, "codec"); FsCodec *codec = NULL; gchar *tmp; g_assert (val); codec = g_value_get_boxed (val); tmp = fs_codec_to_string (codec); g_print ("Send codec changed: %s\n", tmp); g_free (tmp); } } break; default: break; } return TRUE; } int main (int argc, char **argv) { GMainLoop *loop = NULL; GstElement *pipeline = NULL; GstBus *bus = NULL; const gchar *remoteip; guint localport = 0; guint remoteport = 0; GstElement *conf = NULL; FsParticipant *part = NULL; GError *error = NULL; gst_init (&argc, &argv); if (argc != 4) { g_print ("Usage: %s \n", argv[0]); return 1; } localport = atoi (argv[1]); remoteip = argv[2]; remoteport = atoi (argv[3]); if (!localport || !remoteip || !remoteport) { g_print ("Usage: %s \n", argv[0]); return 2; } loop = g_main_loop_new (NULL, FALSE); pipeline = gst_pipeline_new (NULL); bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); gst_bus_add_watch (bus, async_bus_cb, pipeline); gst_object_unref (bus); conf = gst_element_factory_make ("fsrtpconference", NULL); g_assert (conf); part = fs_conference_new_participant (FS_CONFERENCE (conf), &error); print_error (error); g_assert (part); g_assert (gst_bin_add (GST_BIN (pipeline), conf)); add_audio_session (pipeline, FS_CONFERENCE (conf), 1, part, localport, remoteip, remoteport); g_assert (gst_element_set_state (pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE); g_main_loop_run (loop); g_assert (gst_element_set_state (pipeline, GST_STATE_NULL) != GST_STATE_CHANGE_FAILURE); g_object_unref (part); gst_object_unref (pipeline); g_main_loop_unref (loop); return 0; }