[farsight2/master] Move examples to examples dir
Olivier Crête
olivier.crete at collabora.co.uk
Mon Feb 23 13:47:42 PST 2009
---
.gitignore | 6 +-
Makefile.am | 6 +-
configure.ac | 5 +-
examples/Makefile.am | 2 +
examples/commandline/Makefile.am | 14 +
examples/commandline/simple-call.c | 339 +++++++++++++
examples/gui/Makefile.am | 1 +
examples/gui/fs2-gui.glade | 705 +++++++++++++++++++++++++++
examples/gui/fs2-gui.py | 929 ++++++++++++++++++++++++++++++++++++
examples/gui/fs2_gui_net.py | 456 ++++++++++++++++++
tests/Makefile.am | 4 +-
tests/commandline/Makefile.am | 14 -
tests/commandline/simple-call.c | 339 -------------
tests/gui/Makefile.am | 1 -
tests/gui/fs2-gui.glade | 705 ---------------------------
tests/gui/fs2-gui.py | 929 ------------------------------------
tests/gui/fs2_gui_net.py | 456 ------------------
17 files changed, 2458 insertions(+), 2453 deletions(-)
create mode 100644 examples/Makefile.am
create mode 100644 examples/commandline/Makefile.am
create mode 100644 examples/commandline/simple-call.c
create mode 100644 examples/gui/Makefile.am
create mode 100644 examples/gui/fs2-gui.glade
create mode 100755 examples/gui/fs2-gui.py
create mode 100644 examples/gui/fs2_gui_net.py
delete mode 100644 tests/commandline/Makefile.am
delete mode 100644 tests/commandline/simple-call.c
delete mode 100644 tests/gui/Makefile.am
delete mode 100644 tests/gui/fs2-gui.glade
delete mode 100755 tests/gui/fs2-gui.py
delete mode 100644 tests/gui/fs2_gui_net.py
diff --git a/.gitignore b/.gitignore
index 41cb899..8a1744f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,9 +80,9 @@ tests/check/rtp/sendcodecs
tests/check/utils/binadded
tests/check/elements/rtcpfilter
tests/rtp/codec-discovery
-tests/commandline/simple-call
-tests/gui/*.pyo
-tests/gui/*.pyc
+examples/commandline/simple-call
+examples/gui/*.pyo
+examples/gui/*.pyc
aclocal.m4
autom4te.cache
diff --git a/Makefile.am b/Makefile.am
index 13a165a..4fea98b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,7 +15,8 @@ SUBDIRS = \
docs \
common \
common-modified \
- m4
+ m4 \
+ examples
# pkgconfig
DIST_SUBDIRS = \
@@ -27,7 +28,8 @@ DIST_SUBDIRS = \
docs \
common \
common-modified \
- m4
+ m4 \
+ examples
# pkgconfig
EXTRA_DIST = \
diff --git a/configure.ac b/configure.ac
index b0715e0..e75269e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -413,8 +413,9 @@ dnl pkgconfig/farsight2-uninstalled.pc
tests/Makefile
tests/check/Makefile
tests/rtp/Makefile
-tests/gui/Makefile
-tests/commandline/Makefile
+examples/Makefile
+examples/gui/Makefile
+examples/commandline/Makefile
docs/Makefile
docs/libs/Makefile
docs/plugins/Makefile
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..3099ae2
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,2 @@
+
+SUBDIRS = gui commandline
diff --git a/examples/commandline/Makefile.am b/examples/commandline/Makefile.am
new file mode 100644
index 0000000..d374e67
--- /dev/null
+++ b/examples/commandline/Makefile.am
@@ -0,0 +1,14 @@
+
+noinst_PROGRAMS = simple-call
+
+
+AM_CFLAGS = \
+ -I$(top_srcdir)/gst/fsrtpconference/ \
+ $(FS2_INTERNAL_CFLAGS) \
+ $(FS2_CFLAGS) \
+ $(GST_CFLAGS) \
+ $(CFLAGS)
+
+LDADD = \
+ $(top_builddir)/gst-libs/gst/farsight/libgstfarsight-0.10.la \
+ $(GST_LIBS)
diff --git a/examples/commandline/simple-call.c b/examples/commandline/simple-call.c
new file mode 100644
index 0000000..fbc56fb
--- /dev/null
+++ b/examples/commandline/simple-call.c
@@ -0,0 +1,339 @@
+/* Farsight 2 ad-hoc test for simple calls.
+ *
+ * Copyright (C) 2008 Collabora, Nokia
+ * @author: Olivier Crete <olivier.crete at collabora.co.uk>
+ *
+ * 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 farsight, it assumes that
+ * both ends have the EXACT same list of codec installed in the EXACT same order
+ */
+
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/farsight/fs-conference-iface.h>
+
+#define DEFAULT_AUDIOSRC "alsasrc"
+#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);
+
+
+ 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);
+
+ ses->stream = fs_session_new_stream (ses->session, part, FS_DIRECTION_BOTH,
+ "rawudp", 1, ¶m, &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_set_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));
+
+ res = fs_session_set_codec_preferences (ses->session, codecs, &error);
+ print_error (error);
+ fs_codec_list_destroy (codecs);
+
+
+ g_object_get (ses->session, "codecs", &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, "farsight-error"))
+ {
+ gint error;
+ const gchar *error_msg = gst_structure_get_string (s, "error-msg");
+ const gchar *debug_msg = gst_structure_get_string (s, "debug-msg");
+
+ g_assert (gst_structure_get_enum (s, "error-no", FS_TYPE_ERROR,
+ &error));
+
+ if (FS_ERROR_IS_FATAL (error))
+ g_error ("Farsight fatal error: %d %s %s", error, error_msg,
+ debug_msg);
+ else
+ g_warning ("Farsight non-fatal error: %d %s %s", error, error_msg,
+ debug_msg);
+ }
+ else if (gst_structure_has_name (s, "farsight-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,
+ "farsight-local-candidates-prepared"))
+ {
+ g_print ("Local candidates prepared\n");
+ }
+ else if (gst_structure_has_name (s, "farsight-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, "farsight-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 <local port> <remoteip> <remoteport>\n",
+ argv[0]);
+ return 1;
+ }
+
+ localport = atoi (argv[1]);
+ remoteip = argv[2];
+ remoteport = atoi (argv[3]);
+
+ if (!localport || !remoteip || !remoteport)
+ {
+ g_print ("Usage: %s <local port> <remoteip> <remoteport>\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), "test at ignore",
+ &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;
+}
diff --git a/examples/gui/Makefile.am b/examples/gui/Makefile.am
new file mode 100644
index 0000000..10a8a39
--- /dev/null
+++ b/examples/gui/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = fs2-gui.glade fs2_gui_net.py fs2-gui.py
diff --git a/examples/gui/fs2-gui.glade b/examples/gui/fs2-gui.glade
new file mode 100644
index 0000000..b1b4151
--- /dev/null
+++ b/examples/gui/fs2-gui.glade
@@ -0,0 +1,705 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.0 on Tue Jun 17 16:01:07 2008 -->
+<glade-interface>
+ <widget class="GtkWindow" id="main_window">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">6</property>
+ <property name="resizable">False</property>
+ <signal name="destroy" handler="shutdown"/>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkLabel" id="info_label">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">You are now connected to</property>
+ <property name="use_markup">True</property>
+ <property name="use_underline">True</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="users_table">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Video:</property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="x_padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="video_combobox">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="changed" handler="video_combobox_changed_cb"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="audio_combobox">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="changed" handler="audio_combobox_changed_cb"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Audio:</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="x_padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="preview_aspectframe">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">6</property>
+ <property name="label_xalign">0</property>
+ <property name="ratio">1.3300000429153442</property>
+ <child>
+ <widget class="GtkDrawingArea" id="preview_drawingarea">
+ <property name="width_request">160</property>
+ <property name="height_request">120</property>
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="expose_event" handler="exposed"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="frame_label">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes"><b>User</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">GTK_BUTTONBOX_EDGE</property>
+ <child>
+ <widget class="GtkButton" id="dtmf_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">DTMF</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="show_dtmf"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="is_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-quit</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-4</property>
+ <signal name="clicked" handler="shutdown"/>
+ </widget>
+ <packing>
+ <property name="pack_type">GTK_PACK_END</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="window1">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkAspectFrame" id="user_frame">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">6</property>
+ <property name="label_xalign">0</property>
+ <property name="ratio">1.3300000429153442</property>
+ <child>
+ <widget class="GtkDrawingArea" id="user_drawingarea">
+ <property name="width_request">160</property>
+ <property name="height_request">120</property>
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <signal name="expose_event" handler="exposed"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="frame_label">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes"><b>User</b></property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkDialog" id="neworconnect_dialog">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="resizable">False</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <signal name="close" handler="quit"/>
+ <signal name="destroy" handler="quit"/>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">12</property>
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="yalign">0</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">start-here</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkEntry" id="newip_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="text" translatable="yes">127.0.0.1</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Port: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">IP address: </property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Enter the IP address and the port of the server to connect to, or only the port if you want to start a new server</property>
+ <property name="wrap">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkSpinButton" id="newport_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="max_length">5</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">9392 0 65535 1 10 10</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="numeric">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="button7">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">-4</property>
+ <signal name="clicked" handler="quit"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="response_id">2</property>
+ <signal name="clicked" handler="new_server"/>
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="stock">gtk-new</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">_New server</property>
+ <property name="use_underline">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-connect</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">1</property>
+ <signal name="clicked" handler="connect"/>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkWindow" id="dtmf_window">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">Send DTMF</property>
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Send DTMF as:</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="dtmf_as_event">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">RFC-4733 DTMF Event </property>
+ <property name="response_id">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkRadioButton" id="dtmf_as_sound">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Sound</property>
+ <property name="response_id">0</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">dtmf_as_event</property>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">6</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkButton" id="dtmf_pound">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">#</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="#"/>
+ <signal name="released" handler="dtmf_stop" object="#"/>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_0">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">0</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="0"/>
+ <signal name="released" handler="dtmf_stop" object="0"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_star">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">*</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="*"/>
+ <signal name="released" handler="dtmf_stop" object="*"/>
+ </widget>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_9">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">9</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="9"/>
+ <signal name="released" handler="dtmf_stop" object="9"/>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_8">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">8</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="8"/>
+ <signal name="released" handler="dtmf_stop" object="8"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_7">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">7</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="7"/>
+ <signal name="released" handler="dtmf_stop" object="7"/>
+ </widget>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">6</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="6"/>
+ <signal name="released" handler="dtmf_stop" object="6"/>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">5</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="5"/>
+ <signal name="released" handler="dtmf_stop" object="5"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">4</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="4"/>
+ <signal name="released" handler="dtmf_stop" object="4"/>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">3</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="3"/>
+ <signal name="released" handler="dtmf_stop" object="3"/>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">2</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="2"/>
+ <signal name="released" handler="dtmf_stop" object="2"/>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkButton" id="dtmf_1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">1</property>
+ <property name="response_id">0</property>
+ <signal name="pressed" handler="dtmf_start" object="1"/>
+ <signal name="released" handler="dtmf_stop" object="1"/>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">gtk-close</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ <signal name="clicked" handler="dtmf_destroy"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/examples/gui/fs2-gui.py b/examples/gui/fs2-gui.py
new file mode 100755
index 0000000..f744618
--- /dev/null
+++ b/examples/gui/fs2-gui.py
@@ -0,0 +1,929 @@
+#!/usr/bin/python
+
+# Farsight 2 demo GUI program
+#
+# Copyright (C) 2007 Collabora, Nokia
+# @author: Olivier Crete <olivier.crete at collabora.co.uk>
+#
+# 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
+#
+
+import sys, os, pwd, os.path
+import socket
+import threading
+import weakref
+
+import signal
+
+try:
+ import pygtk
+ pygtk.require("2.0")
+
+ import gtk, gtk.glade, gobject, gtk.gdk
+ import gobject
+except ImportError, e:
+ raise SystemExit("PyGTK couldn't be found ! (%s)" % (e[0]))
+
+try:
+ import pygst
+ pygst.require('0.10')
+
+ import gst
+except ImportError, e:
+ raise SystemExit("Gst-Python couldn't be found! (%s)" % (e[0]))
+try:
+ import farsight
+except:
+ try:
+ sys.path.append(os.path.join(os.path.dirname(__file__),
+ '..', '..', 'python', '.libs'))
+ import farsight
+ except ImportError, e:
+ raise SystemExit("Farsight couldn't be found! (%s)" % (e[0]))
+
+
+
+from fs2_gui_net import FsUIClient, FsUIListener, FsUIServer
+
+CAMERA=False
+
+AUDIO=True
+VIDEO=True
+
+CLIENT=1
+SERVER=2
+
+TRANSMITTER="rawudp"
+
+mycname = "".join((pwd.getpwuid(os.getuid())[0],
+ "-" ,
+ str(os.getpid()),
+ "@",
+ socket.gethostname()))
+
+gladefile = os.path.join(os.path.dirname(__file__),"fs2-gui.glade")
+
+
+def make_video_sink(pipeline, xid, name, async=True):
+ "Make a bin with a video sink in it, that will be displayed on xid."
+ bin = gst.Bin("videosink_%d" % xid)
+ sink = gst.element_factory_make("ximagesink", name)
+ sink.set_property("sync", async)
+ sink.set_property("async", async)
+ bin.add(sink)
+ colorspace = gst.element_factory_make("ffmpegcolorspace")
+ bin.add(colorspace)
+ videoscale = gst.element_factory_make("videoscale")
+ bin.add(videoscale)
+ videoscale.link(colorspace)
+ colorspace.link(sink)
+ bin.add_pad(gst.GhostPad("sink", videoscale.get_pad("sink")))
+ sink.set_data("xid", xid)
+ return bin
+
+
+class FsUIPipeline:
+ "Object to wrap the GstPipeline"
+
+ def int_handler(self, sig, frame):
+ try:
+ gst.DEBUG_BIN_TO_DOT_FILE(self.pipeline, 0, "pipelinedump")
+ except:
+ pass
+ sys.exit(2)
+
+ def __init__(self, elementname="fsrtpconference"):
+ self.pipeline = gst.Pipeline()
+ signal.signal(signal.SIGINT, self.int_handler)
+ notifier = farsight.ElementAddedNotifier()
+ notifier.connect("element-added", self.element_added_cb)
+ notifier.add(self.pipeline)
+ self.pipeline.get_bus().set_sync_handler(self.sync_handler)
+ self.pipeline.get_bus().add_watch(self.async_handler)
+ self.conf = gst.element_factory_make(elementname)
+ # Sets lets our own cname
+ self.conf.set_property("sdes-cname", mycname)
+ self.pipeline.add(self.conf)
+ if VIDEO:
+ self.videosource = FsUIVideoSource(self.pipeline)
+ self.videosession = FsUISession(self.conf, self.videosource)
+ if AUDIO:
+ self.audiosource = FsUIAudioSource(self.pipeline)
+ self.audiosession = FsUISession(self.conf, self.audiosource)
+ self.adder = None
+ self.pipeline.set_state(gst.STATE_PLAYING)
+
+ def __del__(self):
+ self.pipeline.set_state(gst.STATE_NULL)
+
+ def sync_handler(self, bus, message):
+ "Message handler to get the prepare-xwindow-id event"
+ if message.type == gst.MESSAGE_ELEMENT and \
+ message.structure.has_name("prepare-xwindow-id"):
+ xid = None
+ element = message.src
+ # We stored the XID on the element or its parent on the expose event
+ # Now lets look it up
+ while not xid and element:
+ xid = element.get_data("xid")
+ element = element.get_parent()
+ if xid:
+ message.src.set_xwindow_id(xid)
+ return gst.BUS_DROP
+ return gst.BUS_PASS
+
+ def async_handler(self, bus, message):
+ "Async handler to print messages"
+ if message.type == gst.MESSAGE_ERROR:
+ print message.src.get_name(), ": ", message.parse_error()
+ elif message.type == gst.MESSAGE_WARNING:
+ print message.src.get_name(), ": ", message.parse_warning()
+ elif message.type == gst.MESSAGE_ELEMENT:
+ if message.structure.has_name("dtmf-event"):
+ print "dtmf-event: %d" % message.structure["number"]
+ elif message.structure.has_name("farsight-local-candidates-prepared"):
+ message.structure["stream"].uistream.local_candidates_prepared()
+
+ elif message.structure.has_name("farsight-new-local-candidate"):
+ message.structure["stream"].uistream.new_local_candidate(
+ message.structure["candidate"])
+ elif message.structure.has_name("farsight-codecs-changed"):
+ print message.src.get_name(), ": ", message.structure.get_name()
+ message.structure["session"].uisession.codecs_changed()
+ if AUDIO and message.structure["session"] == self.audiosession.fssession:
+ self.codecs_changed_audio()
+ if VIDEO and message.structure["session"] == self.videosession.fssession:
+ self.codecs_changed_video()
+ elif message.structure.has_name("farsight-send-codec-changed"):
+ print message.src.get_name(), ": ", message.structure.get_name()
+ print "send codec changed: " + message.structure["codec"].to_string()
+ if AUDIO and message.structure["session"] == self.audiosession.fssession:
+ self.codecs_changed_audio()
+ if VIDEO and message.structure["session"] == self.videosession.fssession:
+ self.codecs_changed_video()
+ elif message.structure.has_name("farsight-recv-codecs-changed"):
+ print message.src.get_name(), ": ", message.structure.get_name()
+ message.structure["stream"].uistream.recv_codecs_changed( \
+ message.structure["codecs"])
+
+
+ elif message.structure.has_name("farsight-error"):
+ print "Async error ("+ str(message.structure["error-no"]) +"): " + message.structure["error-msg"] +" --- "+ message.structure["debug-msg"]
+ else:
+ print message.src.get_name(), ": ", message.structure.get_name()
+ elif message.type != gst.MESSAGE_STATE_CHANGED \
+ and message.type != gst.MESSAGE_ASYNC_DONE:
+ print message.type
+
+ return True
+
+ def make_video_preview(self, xid, newsize_callback):
+ "Creates the preview sink"
+ self.previewsink = make_video_sink(self.pipeline, xid,
+ "previewvideosink", False)
+ self.pipeline.add(self.previewsink)
+ #Add a probe to wait for the first buffer to find the image size
+ self.havesize = self.previewsink.get_pad("sink").add_buffer_probe(self.have_size,
+ newsize_callback)
+
+ self.previewsink.set_state(gst.STATE_PLAYING)
+ self.videosource.tee.link(self.previewsink)
+ self.pipeline.set_state(gst.STATE_PLAYING)
+ return self.previewsink
+
+ def have_size(self, pad, buffer, callback):
+ "Callback on the first buffer to know the drawingarea size"
+ x = buffer.caps[0]["width"]
+ y = buffer.caps[0]["height"]
+ callback(x,y)
+ self.previewsink.get_pad("sink").remove_buffer_probe(self.havesize)
+ return True
+
+ def link_audio_sink(self, pad):
+ "Link the audio sink to the pad"
+ print >>sys.stderr, "LINKING AUDIO SINK"
+ if not self.adder:
+ audiosink = gst.element_factory_make("alsasink")
+ audiosink.set_property("buffer-time", 50000)
+ self.pipeline.add(audiosink)
+
+ try:
+ self.adder = gst.element_factory_make("liveadder")
+ except gst.ElementNotFoundError:
+ audiosink.set_state(gst.STATE_PLAYING)
+ pad.link(audiosink.get_pad("sink"))
+ return
+ self.pipeline.add(self.adder)
+ audiosink.set_state(gst.STATE_PLAYING)
+ self.adder.link(audiosink)
+ self.adder.set_state(gst.STATE_PLAYING)
+ convert1 = gst.element_factory_make("audioconvert")
+ self.pipeline.add(convert1)
+ resample = gst.element_factory_make("audioresample")
+ self.pipeline.add(resample)
+ convert2 = gst.element_factory_make("audioconvert")
+ self.pipeline.add(convert2)
+ convert1.link(resample)
+ resample.link(convert2)
+ convert2.link(self.adder)
+ pad.link(convert1.get_pad("sink"))
+ convert2.set_state(gst.STATE_PLAYING)
+ resample.set_state(gst.STATE_PLAYING)
+ convert1.set_state(gst.STATE_PLAYING)
+
+ def element_added_cb(self, notifier, bin, element):
+ if element.get_factory().get_name() == "x264enc":
+ element.set_property("byte-stream", True)
+ element.set_property("bitrate", 128)
+ elif element.get_factory().get_name() == "gstrtpbin":
+ element.set_property("latency", 100)
+
+
+class FsUISource:
+ "An abstract generic class for media sources"
+
+ def __init__(self, pipeline):
+ self.pipeline = pipeline
+ self.tee = gst.element_factory_make("tee")
+ pipeline.add(self.tee)
+ self.tee.set_state(gst.STATE_PLAYING)
+
+ self.source = self.make_source()
+ pipeline.add(self.source)
+ self.source.link(self.tee)
+ self.playcount = 0
+
+ def __del__(self):
+ self.source.set_state(gst.STATE_NULL)
+ self.tee.set_state(gst.STATE_NULL)
+ self.pipeline.remove(self.source)
+ self.pipeline.remove(self.tee)
+
+
+ def make_source(self):
+ "Creates and returns the source GstElement"
+ raise NotImplementedError()
+
+
+ def get_type(self):
+ "Returns the FsMediaType of the source."
+ raise NotImplementedError()
+
+ def get_src_pad(self, name="src%d"):
+ "Gets a source pad from the source"
+ queue = gst.element_factory_make("queue")
+ queue.set_property("leaky", 2)
+ queue.set_property("max-size-time", 50*gst.MSECOND)
+ requestpad = self.tee.get_request_pad(name)
+ self.pipeline.add(queue)
+ requestpad.link(queue.get_static_pad("sink"))
+ pad = queue.get_static_pad("src")
+ pad.set_data("requestpad", requestpad)
+ pad.set_data("queue", queue)
+ return pad
+
+ def put_src_pad(self, pad):
+ "Puts the source pad from the source"
+ self.pipeline.remove(pad.get_data("queue"))
+ self.tee.release_request_pad(pad.get_data("requestpad"))
+
+
+class FsUIVideoSource(FsUISource):
+ "A Video source"
+
+ def get_type(self):
+ return farsight.MEDIA_TYPE_VIDEO
+
+ def make_source(self):
+ bin = gst.Bin()
+ if CAMERA:
+ source = gst.element_factory_make("v4l2src")
+ source.set_property("device", CAMERA)
+ bin.add(source)
+ else:
+ source = gst.element_factory_make("videotestsrc")
+ source.set_property("is-live", 1)
+ bin.add(source)
+ overlay = gst.element_factory_make("timeoverlay")
+ overlay.set_property("font-desc", "Sans 32")
+ bin.add(overlay)
+ source.link(overlay)
+ source=overlay
+
+ filter = gst.element_factory_make("capsfilter")
+ filter.set_property("caps", gst.Caps("video/x-raw-yuv , width=[300,500] , height=[200,500], framerate=[20/1,30/1]"))
+ bin.add(filter)
+ source.link(filter)
+
+ videoscale = gst.element_factory_make("videoscale")
+ bin.add(videoscale)
+ filter.link(videoscale)
+
+ bin.add_pad(gst.GhostPad("src", videoscale.get_pad("src")))
+ return bin
+
+
+
+class FsUIAudioSource(FsUISource):
+ "An audio source"
+
+ def get_type(self):
+ return farsight.MEDIA_TYPE_AUDIO
+
+ def make_source(self):
+ source = gst.element_factory_make("audiotestsrc")
+ source.set_property("is-live", True)
+ source.set_property("wave", 5)
+ return source
+ #return gst.element_factory_make("alsasrc")
+ #return gst.element_factory_make("gconfaudiosrc")
+
+
+
+class FsUISession:
+ "This is one session (audio or video depending on the source)"
+
+ def __init__(self, conference, source):
+ self.conference = conference
+ self.source = source
+ self.streams = []
+ self.fssession = conference.new_session(source.get_type())
+ self.fssession.uisession = self
+ if source.get_type() == farsight.MEDIA_TYPE_VIDEO:
+ # We prefer H263-1998 because we know it works
+ # We don't know if the others do work
+ # We know H264 doesn't work for now or anything else
+ # that needs to send config data
+ self.fssession.set_codec_preferences( [ \
+ farsight.Codec(farsight.CODEC_ID_ANY,
+ "THEORA",
+ farsight.MEDIA_TYPE_VIDEO,
+ 90000),
+ farsight.Codec(farsight.CODEC_ID_ANY,
+ "H264",
+ farsight.MEDIA_TYPE_VIDEO,
+ 0),
+ farsight.Codec(farsight.CODEC_ID_ANY,
+ "H263-1998",
+ farsight.MEDIA_TYPE_VIDEO,
+ 0),
+ farsight.Codec(farsight.CODEC_ID_ANY,
+ "H263",
+ farsight.MEDIA_TYPE_VIDEO,
+ 0)
+ ])
+ elif source.get_type() == farsight.MEDIA_TYPE_AUDIO:
+ self.fssession.set_codec_preferences( [ \
+ farsight.Codec(farsight.CODEC_ID_ANY,
+ "PCMA",
+ farsight.MEDIA_TYPE_AUDIO,
+ 0),
+ farsight.Codec(farsight.CODEC_ID_ANY,
+ "PCMU",
+ farsight.MEDIA_TYPE_AUDIO,
+ 0),
+ # The gst speexenc element breaks timestamps
+ farsight.Codec(farsight.CODEC_ID_DISABLE,
+ "SPEEX",
+ farsight.MEDIA_TYPE_AUDIO,
+ 16000),
+ # Sadly, vorbis is not currently compatible with live streaming :-(
+ farsight.Codec(farsight.CODEC_ID_DISABLE,
+ "VORBIS",
+ farsight.MEDIA_TYPE_AUDIO,
+ 0),
+ ])
+
+ self.sourcepad = self.source.get_src_pad()
+ self.sourcepad.link(self.fssession.get_property("sink-pad"))
+
+ def __del__(self):
+ self.sourcepad(unlink)
+ self.source.put_src_pad(self.sourcepad)
+ def __stream_finalized(self, s):
+ self.streams.remove(s)
+
+ def new_stream(self, id, participant):
+ "Creates a new stream for a specific participant"
+ transmitter_params = {}
+ # If its video, we start at port 9078, to make it more easy
+ # to differentiate it in a tcpdump log
+ if self.source.get_type() == farsight.MEDIA_TYPE_VIDEO and \
+ TRANSMITTER == "rawudp":
+ cand = farsight.Candidate()
+ cand.component_id = farsight.COMPONENT_RTP
+ cand.port = 9078
+ transmitter_params["preferred-local-candidates"] = [cand]
+ realstream = self.fssession.new_stream(participant.fsparticipant,
+ farsight.DIRECTION_BOTH,
+ TRANSMITTER, transmitter_params)
+ stream = FsUIStream(id, self, participant, realstream)
+ self.streams.append(weakref.ref(stream, self.__stream_finalized))
+ return stream
+
+ def dtmf_start(self, event, method):
+ if (event == "*"):
+ event = farsight.DTMF_EVENT_STAR
+ elif (event == "#"):
+ event = farsight.DTMF_EVENT_POUND
+ else:
+ event = int(event)
+ self.fssession.start_telephony_event(event, 2, method)
+
+ def dtmf_stop(self, method):
+ self.fssession.stop_telephony_event(method)
+
+ def codecs_changed(self):
+ "Callback from FsSession"
+ for s in self.streams:
+ try:
+ s().codecs_changed()
+ except AttributeError:
+ pass
+
+ def send_stream_codecs(self, codecs, sourcestream):
+ for s in self.streams:
+ stream = s()
+ if stream and stream is not sourcestream:
+ stream.connect.send_codecs(stream.participant.id,
+ sourcestream.id,
+ codecs,
+ sourcestream.participant.id)
+
+class FsUIStream:
+ "One participant in one session"
+
+ def __init__(self, id, session, participant, fsstream):
+ self.id = id
+ self.session = session
+ self.participant = participant
+ self.fsstream = fsstream
+ self.connect = participant.connect
+ self.fsstream.uistream = self
+ self.fsstream.connect("src-pad-added", self.__src_pad_added)
+ self.send_codecs = False
+ self.last_codecs = None
+ self.last_stream_codecs = None
+ self.candidates = []
+
+ def local_candidates_prepared(self):
+ "Callback from FsStream"
+ self.connect.send_candidates_done(self.participant.id, self.id)
+ def new_local_candidate(self, candidate):
+ "Callback from FsStream"
+ self.connect.send_candidate(self.participant.id, self.id, candidate)
+ def __src_pad_added(self, stream, pad, codec):
+ "Callback from FsStream"
+ if self.session.source.get_type() == farsight.MEDIA_TYPE_VIDEO:
+ self.participant.link_video_sink(pad)
+ else:
+ self.participant.pipeline.link_audio_sink(pad)
+
+ def candidate(self, candidate):
+ "Callback for the network object."
+ self.candidates.append(candidate)
+ def candidates_done(self):
+ "Callback for the network object."
+ self.fsstream.set_remote_candidates(self.candidates)
+ self.candidates = []
+ def codecs(self, codecs):
+ "Callback for the network object. Set the codecs"
+
+ print "Remote codecs"
+ for c in codecs:
+ print "Got remote codec from %s/%s %s" % \
+ (self.participant.id, self.id, c.to_string())
+ oldcodecs = self.fsstream.get_property("remote-codecs")
+ if oldcodecs == codecs:
+ return
+ try:
+ self.fsstream.set_remote_codecs(codecs)
+ except AttributeError:
+ print "Tried to set codecs with 0 codec"
+ self.send_local_codecs()
+ self.send_stream_codecs()
+
+
+ def send_local_codecs(self):
+ "Callback for the network object."
+ self.send_codecs = True
+ self.check_send_local_codecs()
+
+ def codecs_changed(self):
+ self.check_send_local_codecs()
+ self.send_stream_codecs()
+
+ def check_send_local_codecs(self):
+ "Internal function to send our local codecs when they're ready"
+ if not self.send_codecs:
+ return
+ if not self.session.fssession.get_property("codecs-ready"):
+ print "Codecs are not ready"
+ return
+ codecs = self.session.fssession.get_property("codecs")
+ assert(codecs is not None and len(codecs) > 0)
+ if (codecs == self.last_codecs):
+ return
+ self.last_codecs = codecs
+ print "sending local codecs"
+ self.connect.send_codecs(self.participant.id, self.id, codecs)
+
+ def send_stream_codecs(self):
+ if not self.connect.is_server:
+ return
+ if not self.session.fssession.get_property("codecs-ready"):
+ return
+ codecs = self.fsstream.get_property("negotiated-codecs")
+ if codecs:
+ self.session.send_stream_codecs(codecs, self)
+
+ def recv_codecs_changed(self, codecs):
+ self.participant.recv_codecs_changed()
+
+
+ def __remove_from_send_codecs_to(self, participant):
+ self.send_codecs_to.remote(participant)
+
+
+ def send_codecs_to(self, participant):
+ codecs = self.fsstream.get_property("negotiated-codecs")
+ print "sending stream %s codecs from %s to %s" % \
+ (self.id, self.participant.id, participant.id)
+ if codecs:
+ participant.connect.send_codecs(participant.id, self.id, codecs,
+ self.participant.id)
+
+
+class FsUIParticipant:
+ "Wraps one FsParticipant, is one user remote contact"
+
+ def __init__(self, connect, id, cname, pipeline, mainui):
+ self.connect = connect
+ self.id = id
+ self.cname = cname
+ self.pipeline = pipeline
+ self.mainui = mainui
+ self.fsparticipant = pipeline.conf.new_participant(cname)
+ self.outcv = threading.Condition()
+ self.funnel = None
+ self.make_widget()
+ self.streams = {}
+ if VIDEO:
+ self.streams[int(farsight.MEDIA_TYPE_VIDEO)] = \
+ pipeline.videosession.new_stream(
+ int(farsight.MEDIA_TYPE_VIDEO), self)
+ if AUDIO:
+ self.streams[int(farsight.MEDIA_TYPE_AUDIO)] = \
+ pipeline.audiosession.new_stream(
+ int(farsight.MEDIA_TYPE_AUDIO), self)
+
+ def candidate(self, media, candidate):
+ "Callback for the network object."
+ self.streams[media].candidate(candidate)
+ def candidates_done(self, media):
+ "Callback for the network object."
+ self.streams[media].candidates_done()
+ def codecs(self, media, codecs):
+ "Callback for the network object."
+ self.streams[media].codecs(codecs)
+ def send_local_codecs(self):
+ "Callback for the network object."
+ for id in self.streams:
+ self.streams[id].send_local_codecs()
+
+ def make_widget(self):
+ "Make the widget of the participant's video stream."
+ gtk.gdk.threads_enter()
+ self.glade = gtk.glade.XML(gladefile, "user_frame")
+ self.userframe = self.glade.get_widget("user_frame")
+ self.glade.get_widget("frame_label").set_text(self.cname)
+ self.glade.signal_autoconnect(self)
+ self.label = gtk.Label()
+ self.label.set_alignment(0,0)
+ self.label.show()
+ self.mainui.hbox_add(self.userframe, self.label)
+ gtk.gdk.threads_leave()
+
+ def exposed(self, widget, *args):
+ """From the exposed signal, used to create the video sink
+ The video sink will be created here, but will only be linked when the
+ pad arrives and link_video_sink() is called.
+ """
+ if not VIDEO:
+ return
+ try:
+ self.videosink.get_by_interface(gst.interfaces.XOverlay).expose()
+ except AttributeError:
+ try:
+ self.outcv.acquire()
+ self.videosink = make_video_sink(self.pipeline.pipeline,
+ widget.window.xid,
+ "uservideosink")
+ self.pipeline.pipeline.add(self.videosink)
+ self.funnel = gst.element_factory_make("fsfunnel")
+ self.pipeline.pipeline.add(self.funnel)
+ self.funnel.link(self.videosink)
+ self.havesize = self.videosink.get_pad("sink").add_buffer_probe(self.have_size)
+
+ self.videosink.set_state(gst.STATE_PLAYING)
+ self.funnel.set_state(gst.STATE_PLAYING)
+ self.outcv.notifyAll()
+ finally:
+ self.outcv.release()
+
+
+ def have_size(self, pad, buffer):
+ "Callback on the first buffer to know the drawingarea size"
+ x = buffer.caps[0]["width"]
+ y = buffer.caps[0]["height"]
+ gtk.gdk.threads_enter()
+ self.glade.get_widget("user_drawingarea").set_size_request(x,y)
+ gtk.gdk.threads_leave()
+ self.videosink.get_pad("sink").remove_buffer_probe(self.havesize)
+ del self.havesize
+ return True
+
+
+
+ def link_video_sink(self, pad):
+ """Link the video sink
+
+ Wait for the funnnel for the video sink to be created, when it has been
+ created, link it.
+ """
+ try:
+ self.outcv.acquire()
+ while self.funnel is None:
+ self.outcv.wait()
+ print >>sys.stderr, "LINKING VIDEO SINK"
+ pad.link(self.funnel.get_pad("sink%d"))
+ finally:
+ self.outcv.release()
+
+ def destroy(self):
+ if VIDEO:
+ try:
+ self.videosink.get_pad("sink").disconnect_handler(self.havesize)
+ pass
+ except AttributeError:
+ pass
+ self.glade.get_widget("user_drawingarea").disconnect_by_func(self.exposed)
+ self.streams = {}
+ self.outcv.acquire()
+ self.videosink.set_locked_state(True)
+ self.funnel.set_locked_state(True)
+ self.videosink.set_state(gst.STATE_NULL)
+ self.funnel.set_state(gst.STATE_NULL)
+ self.pipeline.pipeline.remove(self.videosink)
+ self.pipeline.pipeline.remove(self.funnel)
+ del self.videosink
+ del self.funnel
+ self.outcv.release()
+ gtk.gdk.threads_enter()
+ self.userframe.destroy()
+ self.label.destroy()
+ gtk.gdk.threads_leave()
+
+ def error(self):
+ "Callback for the network object."
+ if self.id == 1:
+ self.mainui.fatal_error("<b>Disconnected from server</b>")
+ else:
+ print "ERROR ON %d" % (self.id)
+
+ def recv_codecs_changed(self):
+ codecs = {}
+ for s in self.streams:
+ codec = self.streams[s].fsstream.get_property("current-recv-codecs")
+ mediatype = self.streams[s].session.fssession.get_property("media-type")
+ if len(codec):
+ if mediatype in codecs:
+ codecs[mediatype] += codec
+ else:
+ codecs[mediatype] = codec
+ str = ""
+ for mt in codecs:
+ str += "<big>" +mt.value_nick.title() + "</big>:\n"
+ for c in codecs[mt]:
+ str += " <b>%s</b>: %s %s\n" % (c.id,
+ c.encoding_name,
+ c.clock_rate)
+ self.label.set_markup(str)
+
+ def send_codecs_to(self, participant):
+ for sid in self.streams:
+ self.streams[sid].send_codecs_to(participant)
+
+
+class FsMainUI:
+ "The main UI and its different callbacks"
+
+ def __init__(self, mode, ip, port):
+ self.mode = mode
+ self.pipeline = FsUIPipeline()
+ self.pipeline.codecs_changed_audio = self.reset_audio_codecs
+ self.pipeline.codecs_changed_video = self.reset_video_codecs
+ self.glade = gtk.glade.XML(gladefile, "main_window")
+ self.glade.signal_autoconnect(self)
+ self.mainwindow = self.glade.get_widget("main_window")
+ self.audio_combobox = self.glade.get_widget("audio_combobox")
+ self.video_combobox = self.glade.get_widget("video_combobox")
+ liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+ self.audio_combobox.set_model(liststore)
+ cell = gtk.CellRendererText()
+ self.audio_combobox.pack_start(cell, True)
+ self.audio_combobox.add_attribute(cell, 'text', 0)
+ self.reset_audio_codecs()
+ liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
+ self.video_combobox.set_model(liststore)
+ cell = gtk.CellRendererText()
+ self.video_combobox.pack_start(cell, True)
+ self.video_combobox.add_attribute(cell, 'text', 0)
+ self.reset_video_codecs()
+
+ if mode == CLIENT:
+ self.client = FsUIClient(ip, port, mycname, FsUIParticipant,
+ self.pipeline, self)
+ self.glade.get_widget("info_label").set_markup(
+ "<b>%s</b>\nConnected to %s:%s" % (mycname, ip, port))
+ elif mode == SERVER:
+ self.server = FsUIListener(port, FsUIServer, mycname,
+ FsUIParticipant, self.pipeline, self)
+ self.glade.get_widget("info_label").set_markup(
+ "<b>%s</b>\nExpecting connections on port %s" %
+ (mycname, self.server.port))
+
+
+ self.mainwindow.show()
+
+ def reset_codecs(self, combobox, fssession):
+ liststore = combobox.get_model()
+ current = fssession.get_property("current-send-codec")
+ liststore.clear()
+ for c in fssession.get_property("codecs"):
+ str = ("%s: %s/%s %s" % (c.id,
+ c.media_type.value_nick,
+ c.encoding_name,
+ c.clock_rate))
+ iter = liststore.append([str, c])
+ if current and c and current.id == c.id:
+ combobox.set_active_iter(iter)
+ print "active: "+ c.to_string()
+
+ def reset_audio_codecs(self):
+ if AUDIO:
+ self.reset_codecs(self.audio_combobox,
+ self.pipeline.audiosession.fssession)
+
+ def reset_video_codecs(self):
+ if VIDEO:
+ self.reset_codecs(self.video_combobox,
+ self.pipeline.videosession.fssession)
+
+ def combobox_changed_cb(self, combobox, fssession):
+ liststore = combobox.get_model()
+ iter = combobox.get_active_iter()
+ if iter:
+ codec = liststore.get_value(iter, 1)
+ fssession.set_send_codec(codec)
+
+ def audio_combobox_changed_cb(self, combobox):
+ self.combobox_changed_cb(combobox, self.pipeline.audiosession.fssession)
+
+ def video_combobox_changed_cb(self, combobox):
+ self.combobox_changed_cb(combobox, self.pipeline.videosession.fssession)
+
+
+ def exposed(self, widget, *args):
+ "Callback from the exposed event of the widget to make the preview sink"
+ if not VIDEO:
+ return
+ try:
+ self.preview.get_by_interface(gst.interfaces.XOverlay).expose()
+ except AttributeError:
+ self.preview = self.pipeline.make_video_preview(widget.window.xid,
+ self.newsize)
+
+ def newsize (self, x, y):
+ self.glade.get_widget("preview_drawingarea").set_size_request(x,y)
+
+ def shutdown(self, widget=None):
+ gtk.main_quit()
+
+ def hbox_add(self, widget, label):
+ table = self.glade.get_widget("users_table")
+ x = table.get_properties("n-columns")[0]
+ table.attach(widget, x, x+1, 0, 1)
+ table.attach(label, x, x+1, 1, 3, xpadding=6)
+
+ def __del__(self):
+ self.mainwindow.destroy()
+
+ def fatal_error(self, errormsg):
+ gtk.gdk.threads_enter()
+ dialog = gtk.MessageDialog(self.mainwindow,
+ gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK)
+ dialog.set_markup(errormsg);
+ dialog.run()
+ dialog.destroy()
+ gtk.main_quit()
+ gtk.gdk.threads_leave()
+
+ def show_dtmf(self, button):
+ try:
+ self.dtmf.present()
+ except AttributeError:
+ self.dtmf = gtk.glade.XML(gladefile, "dtmf_window")
+ self.dtmf.signal_autoconnect(self)
+
+ def dtmf_start(self, button):
+ if (self.dtmf.get_widget("dtmf_as_event").get_active()):
+ self.dtmf_last_method = farsight.DTMF_METHOD_RTP_RFC4733
+ elif (self.dtmf.get_widget("dtmf_as_sound").get_active()):
+ self.dtmf_last_method = farsight.DTMF_METHOD_IN_BAND
+ else:
+ print "Invalid DTMF Method"
+ return
+ self.pipeline.audiosession.dtmf_start(button.get_label(), \
+ self.dtmf_last_method)
+
+ def dtmf_stop(self, button):
+ try:
+ self.pipeline.audiosession.dtmf_stop(self.dtmf_last_method)
+ del self.dtmf_last_method
+ except AttributeError:
+ pass
+ def dtmf_destroy(self, button):
+ self.dtmf.get_widget("dtmf_window").destroy()
+ del self.dtmf
+
+
+
+class FsUIStartup:
+ "Displays the startup window and then creates the FsMainUI"
+
+ def __init__(self):
+ self.glade = gtk.glade.XML(gladefile, "neworconnect_dialog")
+ self.dialog = self.glade.get_widget("neworconnect_dialog")
+ self.glade.get_widget("newport_spinbutton").set_value(9893)
+ self.glade.signal_autoconnect(self)
+ self.dialog.show()
+ self.acted = False
+
+ def action(self, mode):
+ port = self.glade.get_widget("newport_spinbutton").get_value_as_int()
+ ip = self.glade.get_widget("newip_entry").get_text()
+ try:
+ self.ui = FsMainUI(mode, ip, port)
+ self.acted = True
+ self.dialog.destroy()
+ del self.glade
+ del self.dialog
+ except socket.error, e:
+ dialog = gtk.MessageDialog(self.dialog,
+ gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK)
+ dialog.set_markup("<b>Could not connect to %s %d</b>" % (ip,port))
+ dialog.format_secondary_markup(e[1])
+ dialog.run()
+ dialog.destroy()
+
+ def new_server(self, widget):
+ self.action(SERVER)
+
+ def connect(self, widget):
+ self.action(CLIENT)
+
+
+ def quit(self, widget):
+ if not self.acted:
+ gtk.main_quit()
+
+
+
+
+if __name__ == "__main__":
+ if len(sys.argv) >= 2:
+ CAMERA = sys.argv[1]
+ else:
+ CAMERA = None
+
+ gobject.threads_init()
+ gtk.gdk.threads_init()
+ startup = FsUIStartup()
+ gtk.main()
diff --git a/examples/gui/fs2_gui_net.py b/examples/gui/fs2_gui_net.py
new file mode 100644
index 0000000..c834d77
--- /dev/null
+++ b/examples/gui/fs2_gui_net.py
@@ -0,0 +1,456 @@
+#!/usr/bin/python
+
+# Farsight 2 simple network signalling library for the demo GUI
+#
+# Copyright (C) 2007 Collabora, Nokia
+# @author: Olivier Crete <olivier.crete at collabora.co.uk>
+#
+# 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
+#
+
+#
+# This is the signalling code used by fs2-gui.py
+#
+
+import sys, os, pwd, os.path
+import socket, struct
+import gc
+
+
+try:
+ import pygst
+ pygst.require('0.10')
+
+ import gst
+except ImportError, e:
+ raise SystemExit("Gst-Python couldn't be found! (%s)" % (e[0]))
+
+try:
+ import farsight
+except:
+ sys.path.append(os.path.join(os.path.dirname(__file__),
+ '..', '..', 'python', '.libs'))
+ import farsight
+
+import gobject
+
+class FsUIConnect:
+ ERROR = 0
+ CODECS = 1
+ CANDIDATE = 2
+ CANDIDATES_DONE = 3
+ INTRO = 4
+
+ def __reset(self):
+ self.type = None
+ self.media = None
+ self.size = struct.calcsize("!IIIIII")
+ self.data = ""
+ self.dest = -1
+ self.src = -1
+
+
+ def __init__(self, sock, callbacks, myid=0):
+ self.sock = sock
+ self.__reset()
+ self.callbacks = callbacks
+ self.myid = myid
+ self.partid = 1
+ self.is_server = True
+ sock.setblocking(0)
+ gobject.io_add_watch(self.sock.fileno(), gobject.IO_IN,
+ self.__data_in)
+ gobject.io_add_watch(self.sock.fileno(),
+ gobject.IO_ERR | gobject.IO_HUP,
+ self.__error)
+
+ def __error(self, source, condition):
+ print "have error"
+ if (self.src >= 0):
+ self.callbacks[self.ERROR](self.src)
+ else:
+ self.callbacks[self.ERROR](self.partid)
+ return False
+
+ def __data_in(self, source, condition):
+ data = self.sock.recv(self.size-len(self.data))
+
+ if len(data) == 0:
+ print "received nothing"
+ if (self.src >= 0):
+ self.callbacks[self.ERROR](self.src)
+ else:
+ self.callbacks[self.ERROR](self.partid)
+ return False
+
+ self.data += data
+ if len(self.data) == self.size:
+ if self.type is not None:
+ if self.type == self.CODECS:
+ data = self.__codecs_from_string(data)
+ elif self.type == self.CANDIDATE:
+ data = self.__candidate_from_string(data)
+ else:
+ data = self.data
+ self.callbacks[self.type](self.src, self.dest,
+ self.media, data)
+ self.__reset()
+ else:
+ (check,
+ self.src,
+ self.dest,
+ self.type,
+ self.media,
+ self.size) = struct.unpack("!IIIIII", self.data)
+ if check != 0xDEADBEEF:
+ print "CORRUPTION"
+ sys.exit(1)
+ if self.myid > 1 and self.dest != self.myid:
+ print "GOT MESSAGE FOR %d, but I am %d" % (self.dest,
+ self.myid)
+ sys.exit(1)
+ self.data=""
+ if self.size == 0:
+ self.callbacks[self.type](self.src, self.dest,
+ self.media, None)
+ self.__reset()
+ return True
+
+ def __send_data(self, dest, type, media=0, data="", src=None):
+ if src is None: src = self.myid
+ if src == 0 and type != self.INTRO: raise Exception
+ try:
+ self.sock.sendall(struct.pack("!IIIIII",
+ 0xDEADBEEF,
+ int(src),
+ int(dest),
+ int(type),
+ int(media),
+ len(data)))
+ self.sock.sendall(data)
+ except socket.error:
+ print "have error"
+ self.callbacks[self.ERROR](self.partid)
+
+
+ def send_error(self, dest, src):
+ self.__send_data(dest, self.ERROR, src=src)
+ def send_intro(self, dest, cname, src=None):
+ self.__send_data(dest, self.INTRO, data=cname, src=src)
+ def send_codecs(self, dest, media, codecs, src=None):
+ self.__send_data(dest, self.CODECS,
+ media=media,
+ data=self.__codecs_to_string(codecs), src=src)
+ def send_candidate(self, dest, media, candidate, src=None):
+ self.__send_data(dest, self.CANDIDATE, media=media,
+ data=self.__candidate_to_string(candidate), src=src)
+ def send_candidates_done(self, dest, media, src=None):
+ self.__send_data(dest, self.CANDIDATES_DONE, media=media, src=src)
+
+ def __del__(self):
+ try:
+ self.sock.close()
+ except AttributeError:
+ pass
+
+
+ def __candidate_to_string(self, candidate):
+ return "|".join((
+ candidate.foundation,
+ str(candidate.component_id),
+ candidate.ip,
+ str(candidate.port),
+ candidate.base_ip,
+ str(candidate.base_port),
+ str(int(candidate.proto)),
+ str(candidate.priority),
+ str(int(candidate.type)),
+ candidate.username,
+ candidate.password))
+
+ def __candidate_from_string(self, string):
+ candidate = farsight.Candidate()
+ (candidate.foundation,
+ component_id,
+ candidate.ip,
+ port,
+ candidate.base_ip,
+ base_port,
+ proto,
+ priority,
+ type,
+ candidate.username,
+ candidate.password) = string.split("|")
+ candidate.component_id = int(component_id)
+ candidate.port = int(port)
+ candidate.base_port = int(base_port)
+ candidate.proto = int(proto)
+ candidate.priority = int(priority)
+ candidate.type = int(type)
+ return candidate
+
+ def __codecs_to_string(self, codecs):
+ codec_strings = []
+ for codec in codecs:
+ start = " ".join((str(codec.id),
+ codec.encoding_name,
+ str(int(codec.media_type)),
+ str(codec.clock_rate),
+ str(codec.channels)))
+ codec = "".join((start,
+ "|",
+ ";".join(["=".join(i) for i in codec.optional_params])))
+ codec_strings.append(codec)
+
+ return "\n".join(codec_strings)
+
+
+ def __codecs_from_string(self, string):
+ codecs = []
+ for substring in string.split("\n"):
+ (start,end) = substring.split("|")
+ (id, encoding_name, media_type, clock_rate, channels) = start.split(" ")
+ codec = farsight.Codec(int(id), encoding_name, int(media_type),
+ int(clock_rate))
+ codec.channels = int(channels)
+ if len(end):
+ codec.optional_params = \
+ [tuple(x.split("=",1)) for x in end.split(";") if len(x) > 0]
+ codecs.append(codec)
+ return codecs
+
+class FsUIConnectClient (FsUIConnect):
+ def __init__(self, ip, port, callbacks):
+ sock = socket.socket()
+ sock.connect((ip, port))
+ FsUIConnect.__init__(self, sock, callbacks)
+ self.is_server = False
+
+class FsUIListener:
+ def __init__(self, port, callback, *args):
+ self.sock = socket.socket()
+ self.callback = callback
+ self.args = args
+ bound = False
+ while not bound:
+ try:
+ self.sock.bind(("", port))
+ bound = True
+ except socket.error, e:
+ port += 1
+ self.port = port
+ print "Bound to port ", port
+ self.sock.setblocking(0)
+ gobject.io_add_watch(self.sock.fileno(), gobject.IO_IN, self.data_in)
+ gobject.io_add_watch(self.sock.fileno(),
+ gobject.IO_ERR | gobject.IO_HUP,
+ self.error)
+ self.sock.listen(3)
+
+ def error(self, source, condition):
+ print "Error on listen"
+ sys.exit(1)
+ return False
+
+ def data_in(self, source, condition):
+ (sock,addr) = self.sock.accept()
+ self.callback(sock, *self.args)
+ return True
+
+class FsUIClient:
+ def __init__(self, ip, port, cname, get_participant, *args):
+ self.participants = {}
+ self.get_participant = get_participant
+ self.args = args
+ self.cname = cname
+ self.connect = FsUIConnectClient(ip, port, (self.__error,
+ self.__codecs,
+ self.__candidate,
+ self.__candidate_done,
+ self.__intro))
+ self.connect.send_intro(1, cname)
+
+ def __codecs(self, src, dest, media, data):
+ print "Got codec Src:%d dest:%d data:%s" % (src, dest, data)
+ self.participants[src].codecs(media, data)
+ def __candidate(self, src, dest, media, data):
+ self.participants[src].candidate(media, data)
+ def __candidate_done(self, src, dest, media, data):
+ self.participants[src].candidates_done(media)
+ def __intro(self, src, dest, media, cname):
+ print "Got Intro from %s, I am %d" % (src, dest)
+ if src == 1:
+ self.connect.myid = dest
+ if not self.participants.has_key(src):
+ if src != 1:
+ self.connect.send_intro(src, self.cname)
+ self.participants[src] = self.get_participant(self.connect, src,
+ cname,
+ *self.args)
+ def __error(self, participantid, *arg):
+ print "Client Error", participantid
+ if participantid == 1:
+ # Communication error with server, its over
+ self.participants[participantid].error()
+ else:
+ self.participants[participantid].destroy()
+ del self.participants[participantid]
+ gc.collect()
+
+
+class FsUIServer:
+ nextid = 2
+ participants = {}
+
+ def __init__(self, sock, cname, get_participant, *args):
+ self.cname = cname
+ self.get_participant = get_participant
+ self.args = args
+ self.connect = FsUIConnect(sock, (self.__error,
+ self.__codecs,
+ self.__candidate,
+ self.__candidate_done,
+ self.__intro), 1)
+ def __codecs(self, src, dest, media, data):
+ FsUIServer.participants[src].codecs(media, data)
+ def __candidate(self, src, dest, media, data):
+ if dest == 1:
+ FsUIServer.participants[src].candidate(media, data)
+ else:
+ print data
+ FsUIServer.participants[dest].connect.send_candidate(dest,
+ media,
+ data,
+ src)
+ def __candidate_done(self, src, dest, media, data):
+ if dest == 1:
+ FsUIServer.participants[src].candidates_done(media)
+ else:
+ FsUIServer.participants[dest].connect.send_candidates_done(dest,
+ media,
+ src)
+ def __intro(self, src, dest, media, cname):
+ print "Got Intro from %s to %s" % (src, dest)
+ if src == 0 and dest == 1:
+ newid = FsUIServer.nextid
+ # Forward the introduction to all other participants
+ for pid in FsUIServer.participants:
+ print "Sending from %d to %d" % (newid, pid)
+ FsUIServer.participants[pid].connect.send_intro(pid, cname,
+ newid)
+ self.connect.send_intro(newid, self.cname)
+ self.connect.partid = newid
+ FsUIServer.participants[newid] = self.get_participant(self.connect,
+ newid,
+ cname,
+ *self.args)
+ FsUIServer.participants[newid].send_local_codecs()
+ FsUIServer.nextid += 1
+ elif dest != 1:
+ FsUIServer.participants[dest].connect.send_intro(dest,
+ cname,
+ src)
+ FsUIServer.participants[src].send_codecs_to(
+ FsUIServer.participants[dest])
+ else:
+ print "ERROR SRC != 0"
+
+ def __error(self, participantid, *args):
+ print "Server Error", participantid
+ FsUIServer.participants[participantid].destroy()
+ del FsUIServer.participants[participantid]
+ gc.collect()
+ for pid in FsUIServer.participants:
+ FsUIServer.participants[pid].connect.send_error(pid, participantid)
+
+if __name__ == "__main__":
+ class TestMedia:
+ def __init__(self, pid, id, connect):
+ self.pid = pid
+ self.id = id
+ self.connect = connect
+ candidate = farsight.Candidate()
+ candidate.component_id = 1
+ connect.send_candidate(self.pid, self.id, candidate)
+ connect.send_candidates_done(self.pid, self.id)
+ def candidate(self, candidate):
+ print "Got candidate", candidate
+ def candidates_done(self):
+ print "Got candidate done"
+ def codecs(self, codecs):
+ if self.connect.myid != 1:
+ self.connect.send_codecs(1, self.id,
+ [farsight.Codec(self.connect.myid,
+ "codec-name",
+ self.pid,
+ self.id)])
+
+ def send_local_codecs(self):
+ print "Send local codecs to %s for media %s" % (self.pid, self.id)
+ self.connect.send_codecs(self.pid, self.id,
+ [farsight.Codec(self.connect.myid,
+ "local_codec",
+ self.pid,
+ self.id)])
+ def get_codecs(self):
+ return [farsight.Codec(self.connect.myid,
+ "nego-codecs",
+ self.pid,
+ self.id)]
+
+
+ class TestParticipant:
+ def __init__(self, connect, id, cname, *args):
+ self.id = id
+ self.streams = {1: TestMedia(id, 1, connect),
+ 2: TestMedia(id, 2, connect)}
+ self.cname = cname
+ self.connect = connect
+ print "New Participant %s and cname %s" % (id,cname)
+ def candidate(self, media, candidate):
+ self.streams[media].candidate(candidate)
+ def candidates_done(self, media):
+ self.streams[media].candidates_done()
+ def codecs(self, media, codecs):
+ self.streams[media].codecs(codecs)
+ def send_local_codecs(self):
+ for id in self.streams:
+ self.streams[id].send_local_codecs()
+ def destroy(self):
+ pass
+ def send_codecs_to(self, participant):
+ for sid in self.streams:
+ print "to: %s from: %s" % (str(participant.id), (self.id))
+ participant.connect.send_codecs(participant.id,
+ self.streams[sid].id,
+ self.streams[sid].get_codecs(),
+ self.id)
+ def error(self):
+ print "ERROR"
+ sys.exit(1)
+ def destroy(self):
+ passs
+
+
+ mycname = "test"
+ mainloop = gobject.MainLoop()
+ gobject.threads_init()
+ if len(sys.argv) > 1:
+ client = FsUIClient("127.0.0.1", int(sys.argv[1]),
+ "cname" + sys.argv[1],
+ TestParticipant)
+ else:
+ listener = FsUIListener(9893, FsUIServer, "cnameServ", TestParticipant)
+ mainloop.run()
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5a2161a..8c22271 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,6 +4,6 @@ if HAVE_GST_CHECK
SUBDIRS_CHECK += check
endif
-SUBDIRS = $(SUBDIRS_CHECK) rtp gui commandline
+SUBDIRS = $(SUBDIRS_CHECK) rtp
-DIST_SUBDIRS = check rtp gui commandline
+DIST_SUBDIRS = check rtp
diff --git a/tests/commandline/Makefile.am b/tests/commandline/Makefile.am
deleted file mode 100644
index d374e67..0000000
--- a/tests/commandline/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-
-noinst_PROGRAMS = simple-call
-
-
-AM_CFLAGS = \
- -I$(top_srcdir)/gst/fsrtpconference/ \
- $(FS2_INTERNAL_CFLAGS) \
- $(FS2_CFLAGS) \
- $(GST_CFLAGS) \
- $(CFLAGS)
-
-LDADD = \
- $(top_builddir)/gst-libs/gst/farsight/libgstfarsight-0.10.la \
- $(GST_LIBS)
diff --git a/tests/commandline/simple-call.c b/tests/commandline/simple-call.c
deleted file mode 100644
index fbc56fb..0000000
--- a/tests/commandline/simple-call.c
+++ /dev/null
@@ -1,339 +0,0 @@
-/* Farsight 2 ad-hoc test for simple calls.
- *
- * Copyright (C) 2008 Collabora, Nokia
- * @author: Olivier Crete <olivier.crete at collabora.co.uk>
- *
- * 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 farsight, it assumes that
- * both ends have the EXACT same list of codec installed in the EXACT same order
- */
-
-
-#include <stdlib.h>
-
-#include <glib.h>
-#include <gst/gst.h>
-#include <gst/farsight/fs-conference-iface.h>
-
-#define DEFAULT_AUDIOSRC "alsasrc"
-#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);
-
-
- 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);
-
- ses->stream = fs_session_new_stream (ses->session, part, FS_DIRECTION_BOTH,
- "rawudp", 1, ¶m, &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_set_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));
-
- res = fs_session_set_codec_preferences (ses->session, codecs, &error);
- print_error (error);
- fs_codec_list_destroy (codecs);
-
-
- g_object_get (ses->session, "codecs", &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, "farsight-error"))
- {
- gint error;
- const gchar *error_msg = gst_structure_get_string (s, "error-msg");
- const gchar *debug_msg = gst_structure_get_string (s, "debug-msg");
-
- g_assert (gst_structure_get_enum (s, "error-no", FS_TYPE_ERROR,
- &error));
-
- if (FS_ERROR_IS_FATAL (error))
- g_error ("Farsight fatal error: %d %s %s", error, error_msg,
- debug_msg);
- else
- g_warning ("Farsight non-fatal error: %d %s %s", error, error_msg,
- debug_msg);
- }
- else if (gst_structure_has_name (s, "farsight-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,
- "farsight-local-candidates-prepared"))
- {
- g_print ("Local candidates prepared\n");
- }
- else if (gst_structure_has_name (s, "farsight-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, "farsight-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 <local port> <remoteip> <remoteport>\n",
- argv[0]);
- return 1;
- }
-
- localport = atoi (argv[1]);
- remoteip = argv[2];
- remoteport = atoi (argv[3]);
-
- if (!localport || !remoteip || !remoteport)
- {
- g_print ("Usage: %s <local port> <remoteip> <remoteport>\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), "test at ignore",
- &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;
-}
diff --git a/tests/gui/Makefile.am b/tests/gui/Makefile.am
deleted file mode 100644
index 10a8a39..0000000
--- a/tests/gui/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-EXTRA_DIST = fs2-gui.glade fs2_gui_net.py fs2-gui.py
diff --git a/tests/gui/fs2-gui.glade b/tests/gui/fs2-gui.glade
deleted file mode 100644
index b1b4151..0000000
--- a/tests/gui/fs2-gui.glade
+++ /dev/null
@@ -1,705 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
-<!--Generated with glade3 3.4.0 on Tue Jun 17 16:01:07 2008 -->
-<glade-interface>
- <widget class="GtkWindow" id="main_window">
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">6</property>
- <property name="resizable">False</property>
- <signal name="destroy" handler="shutdown"/>
- <child>
- <widget class="GtkVBox" id="vbox1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="spacing">12</property>
- <child>
- <widget class="GtkLabel" id="info_label">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">You are now connected to</property>
- <property name="use_markup">True</property>
- <property name="use_underline">True</property>
- <property name="justify">GTK_JUSTIFY_CENTER</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkTable" id="users_table">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="n_rows">3</property>
- <property name="n_columns">2</property>
- <property name="column_spacing">6</property>
- <property name="row_spacing">6</property>
- <child>
- <widget class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Video:</property>
- </widget>
- <packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="x_options">GTK_FILL</property>
- <property name="x_padding">6</property>
- </packing>
- </child>
- <child>
- <widget class="GtkComboBox" id="video_combobox">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <signal name="changed" handler="video_combobox_changed_cb"/>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="y_options">GTK_EXPAND</property>
- </packing>
- </child>
- <child>
- <widget class="GtkComboBox" id="audio_combobox">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <signal name="changed" handler="audio_combobox_changed_cb"/>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_EXPAND</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Audio:</property>
- </widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="x_options">GTK_FILL</property>
- <property name="x_padding">6</property>
- </packing>
- </child>
- <child>
- <widget class="GtkAspectFrame" id="preview_aspectframe">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">6</property>
- <property name="label_xalign">0</property>
- <property name="ratio">1.3300000429153442</property>
- <child>
- <widget class="GtkDrawingArea" id="preview_drawingarea">
- <property name="width_request">160</property>
- <property name="height_request">120</property>
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <signal name="expose_event" handler="exposed"/>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="frame_label">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes"><b>User</b></property>
- <property name="use_markup">True</property>
- </widget>
- <packing>
- <property name="type">label_item</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="right_attach">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHButtonBox" id="hbuttonbox1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="spacing">6</property>
- <property name="layout_style">GTK_BUTTONBOX_EDGE</property>
- <child>
- <widget class="GtkButton" id="dtmf_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">DTMF</property>
- <property name="response_id">0</property>
- <signal name="clicked" handler="show_dtmf"/>
- </widget>
- </child>
- <child>
- <widget class="GtkButton" id="button2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="has_focus">True</property>
- <property name="is_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-quit</property>
- <property name="use_stock">True</property>
- <property name="response_id">-4</property>
- <signal name="clicked" handler="shutdown"/>
- </widget>
- <packing>
- <property name="pack_type">GTK_PACK_END</property>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- <widget class="GtkWindow" id="window1">
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkAspectFrame" id="user_frame">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">6</property>
- <property name="label_xalign">0</property>
- <property name="ratio">1.3300000429153442</property>
- <child>
- <widget class="GtkDrawingArea" id="user_drawingarea">
- <property name="width_request">160</property>
- <property name="height_request">120</property>
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <signal name="expose_event" handler="exposed"/>
- </widget>
- </child>
- <child>
- <widget class="GtkLabel" id="frame_label">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes"><b>User</b></property>
- <property name="use_markup">True</property>
- </widget>
- <packing>
- <property name="type">label_item</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- <widget class="GtkDialog" id="neworconnect_dialog">
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">5</property>
- <property name="resizable">False</property>
- <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
- <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
- <property name="has_separator">False</property>
- <signal name="close" handler="quit"/>
- <signal name="destroy" handler="quit"/>
- <child internal-child="vbox">
- <widget class="GtkVBox" id="dialog-vbox2">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="spacing">2</property>
- <child>
- <widget class="GtkTable" id="table3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="border_width">6</property>
- <property name="n_rows">3</property>
- <property name="n_columns">3</property>
- <property name="column_spacing">12</property>
- <property name="row_spacing">12</property>
- <child>
- <widget class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkImage" id="image5">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="yalign">0</property>
- <property name="icon_size">6</property>
- <property name="icon_name">start-here</property>
- </widget>
- </child>
- <child>
- <widget class="GtkEntry" id="newip_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="text" translatable="yes">127.0.0.1</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">3</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label9">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Port: </property>
- </widget>
- <packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label10">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">IP address: </property>
- </widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- <property name="y_options">GTK_FILL</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label11">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Enter the IP address and the port of the server to connect to, or only the port if you want to start a new server</property>
- <property name="wrap">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkSpinButton" id="newport_spinbutton">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="max_length">5</property>
- <property name="width_chars">5</property>
- <property name="adjustment">9392 0 65535 1 10 10</property>
- <property name="snap_to_ticks">True</property>
- <property name="numeric">True</property>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- <property name="x_options">GTK_SHRINK</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child internal-child="action_area">
- <widget class="GtkHButtonBox" id="dialog-action_area2">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="layout_style">GTK_BUTTONBOX_END</property>
- <child>
- <widget class="GtkButton" id="button7">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-close</property>
- <property name="use_stock">True</property>
- <property name="response_id">-4</property>
- <signal name="clicked" handler="quit"/>
- </widget>
- </child>
- <child>
- <widget class="GtkButton" id="button6">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="response_id">2</property>
- <signal name="clicked" handler="new_server"/>
- <child>
- <widget class="GtkHBox" id="hbox3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkImage" id="image3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="stock">gtk-new</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <widget class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">_New server</property>
- <property name="use_underline">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="button5">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-connect</property>
- <property name="use_stock">True</property>
- <property name="response_id">1</property>
- <signal name="clicked" handler="connect"/>
- </widget>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="pack_type">GTK_PACK_END</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
- <widget class="GtkWindow" id="dtmf_window">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="title" translatable="yes">Send DTMF</property>
- <child>
- <widget class="GtkVBox" id="vbox2">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="spacing">12</property>
- <child>
- <widget class="GtkVBox" id="vbox3">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <child>
- <widget class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Send DTMF as:</property>
- </widget>
- </child>
- <child>
- <widget class="GtkRadioButton" id="dtmf_as_event">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">RFC-4733 DTMF Event </property>
- <property name="response_id">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkRadioButton" id="dtmf_as_sound">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">Sound</property>
- <property name="response_id">0</property>
- <property name="draw_indicator">True</property>
- <property name="group">dtmf_as_event</property>
- </widget>
- <packing>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="padding">6</property>
- </packing>
- </child>
- <child>
- <widget class="GtkTable" id="table1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="n_rows">4</property>
- <property name="n_columns">3</property>
- <property name="column_spacing">6</property>
- <property name="row_spacing">6</property>
- <child>
- <widget class="GtkButton" id="dtmf_pound">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">#</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="#"/>
- <signal name="released" handler="dtmf_stop" object="#"/>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_0">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">0</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="0"/>
- <signal name="released" handler="dtmf_stop" object="0"/>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_star">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">*</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="*"/>
- <signal name="released" handler="dtmf_stop" object="*"/>
- </widget>
- <packing>
- <property name="top_attach">3</property>
- <property name="bottom_attach">4</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_9">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">9</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="9"/>
- <signal name="released" handler="dtmf_stop" object="9"/>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_8">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">8</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="8"/>
- <signal name="released" handler="dtmf_stop" object="8"/>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_7">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">7</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="7"/>
- <signal name="released" handler="dtmf_stop" object="7"/>
- </widget>
- <packing>
- <property name="top_attach">2</property>
- <property name="bottom_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_6">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">6</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="6"/>
- <signal name="released" handler="dtmf_stop" object="6"/>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_5">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">5</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="5"/>
- <signal name="released" handler="dtmf_stop" object="5"/>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_4">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">4</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="4"/>
- <signal name="released" handler="dtmf_stop" object="4"/>
- </widget>
- <packing>
- <property name="top_attach">1</property>
- <property name="bottom_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_3">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">3</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="3"/>
- <signal name="released" handler="dtmf_stop" object="3"/>
- </widget>
- <packing>
- <property name="left_attach">2</property>
- <property name="right_attach">3</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_2">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">2</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="2"/>
- <signal name="released" handler="dtmf_stop" object="2"/>
- </widget>
- <packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- </packing>
- </child>
- <child>
- <widget class="GtkButton" id="dtmf_1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">1</property>
- <property name="response_id">0</property>
- <signal name="pressed" handler="dtmf_start" object="1"/>
- <signal name="released" handler="dtmf_stop" object="1"/>
- </widget>
- </child>
- </widget>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <widget class="GtkHBox" id="hbox1">
- <property name="visible">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="spacing">6</property>
- <child>
- <widget class="GtkButton" id="button1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="label" translatable="yes">gtk-close</property>
- <property name="use_stock">True</property>
- <property name="response_id">0</property>
- <signal name="clicked" handler="dtmf_destroy"/>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="padding">6</property>
- <property name="pack_type">GTK_PACK_END</property>
- </packing>
- </child>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="padding">6</property>
- <property name="position">2</property>
- </packing>
- </child>
- </widget>
- </child>
- </widget>
-</glade-interface>
diff --git a/tests/gui/fs2-gui.py b/tests/gui/fs2-gui.py
deleted file mode 100755
index f744618..0000000
--- a/tests/gui/fs2-gui.py
+++ /dev/null
@@ -1,929 +0,0 @@
-#!/usr/bin/python
-
-# Farsight 2 demo GUI program
-#
-# Copyright (C) 2007 Collabora, Nokia
-# @author: Olivier Crete <olivier.crete at collabora.co.uk>
-#
-# 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
-#
-
-import sys, os, pwd, os.path
-import socket
-import threading
-import weakref
-
-import signal
-
-try:
- import pygtk
- pygtk.require("2.0")
-
- import gtk, gtk.glade, gobject, gtk.gdk
- import gobject
-except ImportError, e:
- raise SystemExit("PyGTK couldn't be found ! (%s)" % (e[0]))
-
-try:
- import pygst
- pygst.require('0.10')
-
- import gst
-except ImportError, e:
- raise SystemExit("Gst-Python couldn't be found! (%s)" % (e[0]))
-try:
- import farsight
-except:
- try:
- sys.path.append(os.path.join(os.path.dirname(__file__),
- '..', '..', 'python', '.libs'))
- import farsight
- except ImportError, e:
- raise SystemExit("Farsight couldn't be found! (%s)" % (e[0]))
-
-
-
-from fs2_gui_net import FsUIClient, FsUIListener, FsUIServer
-
-CAMERA=False
-
-AUDIO=True
-VIDEO=True
-
-CLIENT=1
-SERVER=2
-
-TRANSMITTER="rawudp"
-
-mycname = "".join((pwd.getpwuid(os.getuid())[0],
- "-" ,
- str(os.getpid()),
- "@",
- socket.gethostname()))
-
-gladefile = os.path.join(os.path.dirname(__file__),"fs2-gui.glade")
-
-
-def make_video_sink(pipeline, xid, name, async=True):
- "Make a bin with a video sink in it, that will be displayed on xid."
- bin = gst.Bin("videosink_%d" % xid)
- sink = gst.element_factory_make("ximagesink", name)
- sink.set_property("sync", async)
- sink.set_property("async", async)
- bin.add(sink)
- colorspace = gst.element_factory_make("ffmpegcolorspace")
- bin.add(colorspace)
- videoscale = gst.element_factory_make("videoscale")
- bin.add(videoscale)
- videoscale.link(colorspace)
- colorspace.link(sink)
- bin.add_pad(gst.GhostPad("sink", videoscale.get_pad("sink")))
- sink.set_data("xid", xid)
- return bin
-
-
-class FsUIPipeline:
- "Object to wrap the GstPipeline"
-
- def int_handler(self, sig, frame):
- try:
- gst.DEBUG_BIN_TO_DOT_FILE(self.pipeline, 0, "pipelinedump")
- except:
- pass
- sys.exit(2)
-
- def __init__(self, elementname="fsrtpconference"):
- self.pipeline = gst.Pipeline()
- signal.signal(signal.SIGINT, self.int_handler)
- notifier = farsight.ElementAddedNotifier()
- notifier.connect("element-added", self.element_added_cb)
- notifier.add(self.pipeline)
- self.pipeline.get_bus().set_sync_handler(self.sync_handler)
- self.pipeline.get_bus().add_watch(self.async_handler)
- self.conf = gst.element_factory_make(elementname)
- # Sets lets our own cname
- self.conf.set_property("sdes-cname", mycname)
- self.pipeline.add(self.conf)
- if VIDEO:
- self.videosource = FsUIVideoSource(self.pipeline)
- self.videosession = FsUISession(self.conf, self.videosource)
- if AUDIO:
- self.audiosource = FsUIAudioSource(self.pipeline)
- self.audiosession = FsUISession(self.conf, self.audiosource)
- self.adder = None
- self.pipeline.set_state(gst.STATE_PLAYING)
-
- def __del__(self):
- self.pipeline.set_state(gst.STATE_NULL)
-
- def sync_handler(self, bus, message):
- "Message handler to get the prepare-xwindow-id event"
- if message.type == gst.MESSAGE_ELEMENT and \
- message.structure.has_name("prepare-xwindow-id"):
- xid = None
- element = message.src
- # We stored the XID on the element or its parent on the expose event
- # Now lets look it up
- while not xid and element:
- xid = element.get_data("xid")
- element = element.get_parent()
- if xid:
- message.src.set_xwindow_id(xid)
- return gst.BUS_DROP
- return gst.BUS_PASS
-
- def async_handler(self, bus, message):
- "Async handler to print messages"
- if message.type == gst.MESSAGE_ERROR:
- print message.src.get_name(), ": ", message.parse_error()
- elif message.type == gst.MESSAGE_WARNING:
- print message.src.get_name(), ": ", message.parse_warning()
- elif message.type == gst.MESSAGE_ELEMENT:
- if message.structure.has_name("dtmf-event"):
- print "dtmf-event: %d" % message.structure["number"]
- elif message.structure.has_name("farsight-local-candidates-prepared"):
- message.structure["stream"].uistream.local_candidates_prepared()
-
- elif message.structure.has_name("farsight-new-local-candidate"):
- message.structure["stream"].uistream.new_local_candidate(
- message.structure["candidate"])
- elif message.structure.has_name("farsight-codecs-changed"):
- print message.src.get_name(), ": ", message.structure.get_name()
- message.structure["session"].uisession.codecs_changed()
- if AUDIO and message.structure["session"] == self.audiosession.fssession:
- self.codecs_changed_audio()
- if VIDEO and message.structure["session"] == self.videosession.fssession:
- self.codecs_changed_video()
- elif message.structure.has_name("farsight-send-codec-changed"):
- print message.src.get_name(), ": ", message.structure.get_name()
- print "send codec changed: " + message.structure["codec"].to_string()
- if AUDIO and message.structure["session"] == self.audiosession.fssession:
- self.codecs_changed_audio()
- if VIDEO and message.structure["session"] == self.videosession.fssession:
- self.codecs_changed_video()
- elif message.structure.has_name("farsight-recv-codecs-changed"):
- print message.src.get_name(), ": ", message.structure.get_name()
- message.structure["stream"].uistream.recv_codecs_changed( \
- message.structure["codecs"])
-
-
- elif message.structure.has_name("farsight-error"):
- print "Async error ("+ str(message.structure["error-no"]) +"): " + message.structure["error-msg"] +" --- "+ message.structure["debug-msg"]
- else:
- print message.src.get_name(), ": ", message.structure.get_name()
- elif message.type != gst.MESSAGE_STATE_CHANGED \
- and message.type != gst.MESSAGE_ASYNC_DONE:
- print message.type
-
- return True
-
- def make_video_preview(self, xid, newsize_callback):
- "Creates the preview sink"
- self.previewsink = make_video_sink(self.pipeline, xid,
- "previewvideosink", False)
- self.pipeline.add(self.previewsink)
- #Add a probe to wait for the first buffer to find the image size
- self.havesize = self.previewsink.get_pad("sink").add_buffer_probe(self.have_size,
- newsize_callback)
-
- self.previewsink.set_state(gst.STATE_PLAYING)
- self.videosource.tee.link(self.previewsink)
- self.pipeline.set_state(gst.STATE_PLAYING)
- return self.previewsink
-
- def have_size(self, pad, buffer, callback):
- "Callback on the first buffer to know the drawingarea size"
- x = buffer.caps[0]["width"]
- y = buffer.caps[0]["height"]
- callback(x,y)
- self.previewsink.get_pad("sink").remove_buffer_probe(self.havesize)
- return True
-
- def link_audio_sink(self, pad):
- "Link the audio sink to the pad"
- print >>sys.stderr, "LINKING AUDIO SINK"
- if not self.adder:
- audiosink = gst.element_factory_make("alsasink")
- audiosink.set_property("buffer-time", 50000)
- self.pipeline.add(audiosink)
-
- try:
- self.adder = gst.element_factory_make("liveadder")
- except gst.ElementNotFoundError:
- audiosink.set_state(gst.STATE_PLAYING)
- pad.link(audiosink.get_pad("sink"))
- return
- self.pipeline.add(self.adder)
- audiosink.set_state(gst.STATE_PLAYING)
- self.adder.link(audiosink)
- self.adder.set_state(gst.STATE_PLAYING)
- convert1 = gst.element_factory_make("audioconvert")
- self.pipeline.add(convert1)
- resample = gst.element_factory_make("audioresample")
- self.pipeline.add(resample)
- convert2 = gst.element_factory_make("audioconvert")
- self.pipeline.add(convert2)
- convert1.link(resample)
- resample.link(convert2)
- convert2.link(self.adder)
- pad.link(convert1.get_pad("sink"))
- convert2.set_state(gst.STATE_PLAYING)
- resample.set_state(gst.STATE_PLAYING)
- convert1.set_state(gst.STATE_PLAYING)
-
- def element_added_cb(self, notifier, bin, element):
- if element.get_factory().get_name() == "x264enc":
- element.set_property("byte-stream", True)
- element.set_property("bitrate", 128)
- elif element.get_factory().get_name() == "gstrtpbin":
- element.set_property("latency", 100)
-
-
-class FsUISource:
- "An abstract generic class for media sources"
-
- def __init__(self, pipeline):
- self.pipeline = pipeline
- self.tee = gst.element_factory_make("tee")
- pipeline.add(self.tee)
- self.tee.set_state(gst.STATE_PLAYING)
-
- self.source = self.make_source()
- pipeline.add(self.source)
- self.source.link(self.tee)
- self.playcount = 0
-
- def __del__(self):
- self.source.set_state(gst.STATE_NULL)
- self.tee.set_state(gst.STATE_NULL)
- self.pipeline.remove(self.source)
- self.pipeline.remove(self.tee)
-
-
- def make_source(self):
- "Creates and returns the source GstElement"
- raise NotImplementedError()
-
-
- def get_type(self):
- "Returns the FsMediaType of the source."
- raise NotImplementedError()
-
- def get_src_pad(self, name="src%d"):
- "Gets a source pad from the source"
- queue = gst.element_factory_make("queue")
- queue.set_property("leaky", 2)
- queue.set_property("max-size-time", 50*gst.MSECOND)
- requestpad = self.tee.get_request_pad(name)
- self.pipeline.add(queue)
- requestpad.link(queue.get_static_pad("sink"))
- pad = queue.get_static_pad("src")
- pad.set_data("requestpad", requestpad)
- pad.set_data("queue", queue)
- return pad
-
- def put_src_pad(self, pad):
- "Puts the source pad from the source"
- self.pipeline.remove(pad.get_data("queue"))
- self.tee.release_request_pad(pad.get_data("requestpad"))
-
-
-class FsUIVideoSource(FsUISource):
- "A Video source"
-
- def get_type(self):
- return farsight.MEDIA_TYPE_VIDEO
-
- def make_source(self):
- bin = gst.Bin()
- if CAMERA:
- source = gst.element_factory_make("v4l2src")
- source.set_property("device", CAMERA)
- bin.add(source)
- else:
- source = gst.element_factory_make("videotestsrc")
- source.set_property("is-live", 1)
- bin.add(source)
- overlay = gst.element_factory_make("timeoverlay")
- overlay.set_property("font-desc", "Sans 32")
- bin.add(overlay)
- source.link(overlay)
- source=overlay
-
- filter = gst.element_factory_make("capsfilter")
- filter.set_property("caps", gst.Caps("video/x-raw-yuv , width=[300,500] , height=[200,500], framerate=[20/1,30/1]"))
- bin.add(filter)
- source.link(filter)
-
- videoscale = gst.element_factory_make("videoscale")
- bin.add(videoscale)
- filter.link(videoscale)
-
- bin.add_pad(gst.GhostPad("src", videoscale.get_pad("src")))
- return bin
-
-
-
-class FsUIAudioSource(FsUISource):
- "An audio source"
-
- def get_type(self):
- return farsight.MEDIA_TYPE_AUDIO
-
- def make_source(self):
- source = gst.element_factory_make("audiotestsrc")
- source.set_property("is-live", True)
- source.set_property("wave", 5)
- return source
- #return gst.element_factory_make("alsasrc")
- #return gst.element_factory_make("gconfaudiosrc")
-
-
-
-class FsUISession:
- "This is one session (audio or video depending on the source)"
-
- def __init__(self, conference, source):
- self.conference = conference
- self.source = source
- self.streams = []
- self.fssession = conference.new_session(source.get_type())
- self.fssession.uisession = self
- if source.get_type() == farsight.MEDIA_TYPE_VIDEO:
- # We prefer H263-1998 because we know it works
- # We don't know if the others do work
- # We know H264 doesn't work for now or anything else
- # that needs to send config data
- self.fssession.set_codec_preferences( [ \
- farsight.Codec(farsight.CODEC_ID_ANY,
- "THEORA",
- farsight.MEDIA_TYPE_VIDEO,
- 90000),
- farsight.Codec(farsight.CODEC_ID_ANY,
- "H264",
- farsight.MEDIA_TYPE_VIDEO,
- 0),
- farsight.Codec(farsight.CODEC_ID_ANY,
- "H263-1998",
- farsight.MEDIA_TYPE_VIDEO,
- 0),
- farsight.Codec(farsight.CODEC_ID_ANY,
- "H263",
- farsight.MEDIA_TYPE_VIDEO,
- 0)
- ])
- elif source.get_type() == farsight.MEDIA_TYPE_AUDIO:
- self.fssession.set_codec_preferences( [ \
- farsight.Codec(farsight.CODEC_ID_ANY,
- "PCMA",
- farsight.MEDIA_TYPE_AUDIO,
- 0),
- farsight.Codec(farsight.CODEC_ID_ANY,
- "PCMU",
- farsight.MEDIA_TYPE_AUDIO,
- 0),
- # The gst speexenc element breaks timestamps
- farsight.Codec(farsight.CODEC_ID_DISABLE,
- "SPEEX",
- farsight.MEDIA_TYPE_AUDIO,
- 16000),
- # Sadly, vorbis is not currently compatible with live streaming :-(
- farsight.Codec(farsight.CODEC_ID_DISABLE,
- "VORBIS",
- farsight.MEDIA_TYPE_AUDIO,
- 0),
- ])
-
- self.sourcepad = self.source.get_src_pad()
- self.sourcepad.link(self.fssession.get_property("sink-pad"))
-
- def __del__(self):
- self.sourcepad(unlink)
- self.source.put_src_pad(self.sourcepad)
- def __stream_finalized(self, s):
- self.streams.remove(s)
-
- def new_stream(self, id, participant):
- "Creates a new stream for a specific participant"
- transmitter_params = {}
- # If its video, we start at port 9078, to make it more easy
- # to differentiate it in a tcpdump log
- if self.source.get_type() == farsight.MEDIA_TYPE_VIDEO and \
- TRANSMITTER == "rawudp":
- cand = farsight.Candidate()
- cand.component_id = farsight.COMPONENT_RTP
- cand.port = 9078
- transmitter_params["preferred-local-candidates"] = [cand]
- realstream = self.fssession.new_stream(participant.fsparticipant,
- farsight.DIRECTION_BOTH,
- TRANSMITTER, transmitter_params)
- stream = FsUIStream(id, self, participant, realstream)
- self.streams.append(weakref.ref(stream, self.__stream_finalized))
- return stream
-
- def dtmf_start(self, event, method):
- if (event == "*"):
- event = farsight.DTMF_EVENT_STAR
- elif (event == "#"):
- event = farsight.DTMF_EVENT_POUND
- else:
- event = int(event)
- self.fssession.start_telephony_event(event, 2, method)
-
- def dtmf_stop(self, method):
- self.fssession.stop_telephony_event(method)
-
- def codecs_changed(self):
- "Callback from FsSession"
- for s in self.streams:
- try:
- s().codecs_changed()
- except AttributeError:
- pass
-
- def send_stream_codecs(self, codecs, sourcestream):
- for s in self.streams:
- stream = s()
- if stream and stream is not sourcestream:
- stream.connect.send_codecs(stream.participant.id,
- sourcestream.id,
- codecs,
- sourcestream.participant.id)
-
-class FsUIStream:
- "One participant in one session"
-
- def __init__(self, id, session, participant, fsstream):
- self.id = id
- self.session = session
- self.participant = participant
- self.fsstream = fsstream
- self.connect = participant.connect
- self.fsstream.uistream = self
- self.fsstream.connect("src-pad-added", self.__src_pad_added)
- self.send_codecs = False
- self.last_codecs = None
- self.last_stream_codecs = None
- self.candidates = []
-
- def local_candidates_prepared(self):
- "Callback from FsStream"
- self.connect.send_candidates_done(self.participant.id, self.id)
- def new_local_candidate(self, candidate):
- "Callback from FsStream"
- self.connect.send_candidate(self.participant.id, self.id, candidate)
- def __src_pad_added(self, stream, pad, codec):
- "Callback from FsStream"
- if self.session.source.get_type() == farsight.MEDIA_TYPE_VIDEO:
- self.participant.link_video_sink(pad)
- else:
- self.participant.pipeline.link_audio_sink(pad)
-
- def candidate(self, candidate):
- "Callback for the network object."
- self.candidates.append(candidate)
- def candidates_done(self):
- "Callback for the network object."
- self.fsstream.set_remote_candidates(self.candidates)
- self.candidates = []
- def codecs(self, codecs):
- "Callback for the network object. Set the codecs"
-
- print "Remote codecs"
- for c in codecs:
- print "Got remote codec from %s/%s %s" % \
- (self.participant.id, self.id, c.to_string())
- oldcodecs = self.fsstream.get_property("remote-codecs")
- if oldcodecs == codecs:
- return
- try:
- self.fsstream.set_remote_codecs(codecs)
- except AttributeError:
- print "Tried to set codecs with 0 codec"
- self.send_local_codecs()
- self.send_stream_codecs()
-
-
- def send_local_codecs(self):
- "Callback for the network object."
- self.send_codecs = True
- self.check_send_local_codecs()
-
- def codecs_changed(self):
- self.check_send_local_codecs()
- self.send_stream_codecs()
-
- def check_send_local_codecs(self):
- "Internal function to send our local codecs when they're ready"
- if not self.send_codecs:
- return
- if not self.session.fssession.get_property("codecs-ready"):
- print "Codecs are not ready"
- return
- codecs = self.session.fssession.get_property("codecs")
- assert(codecs is not None and len(codecs) > 0)
- if (codecs == self.last_codecs):
- return
- self.last_codecs = codecs
- print "sending local codecs"
- self.connect.send_codecs(self.participant.id, self.id, codecs)
-
- def send_stream_codecs(self):
- if not self.connect.is_server:
- return
- if not self.session.fssession.get_property("codecs-ready"):
- return
- codecs = self.fsstream.get_property("negotiated-codecs")
- if codecs:
- self.session.send_stream_codecs(codecs, self)
-
- def recv_codecs_changed(self, codecs):
- self.participant.recv_codecs_changed()
-
-
- def __remove_from_send_codecs_to(self, participant):
- self.send_codecs_to.remote(participant)
-
-
- def send_codecs_to(self, participant):
- codecs = self.fsstream.get_property("negotiated-codecs")
- print "sending stream %s codecs from %s to %s" % \
- (self.id, self.participant.id, participant.id)
- if codecs:
- participant.connect.send_codecs(participant.id, self.id, codecs,
- self.participant.id)
-
-
-class FsUIParticipant:
- "Wraps one FsParticipant, is one user remote contact"
-
- def __init__(self, connect, id, cname, pipeline, mainui):
- self.connect = connect
- self.id = id
- self.cname = cname
- self.pipeline = pipeline
- self.mainui = mainui
- self.fsparticipant = pipeline.conf.new_participant(cname)
- self.outcv = threading.Condition()
- self.funnel = None
- self.make_widget()
- self.streams = {}
- if VIDEO:
- self.streams[int(farsight.MEDIA_TYPE_VIDEO)] = \
- pipeline.videosession.new_stream(
- int(farsight.MEDIA_TYPE_VIDEO), self)
- if AUDIO:
- self.streams[int(farsight.MEDIA_TYPE_AUDIO)] = \
- pipeline.audiosession.new_stream(
- int(farsight.MEDIA_TYPE_AUDIO), self)
-
- def candidate(self, media, candidate):
- "Callback for the network object."
- self.streams[media].candidate(candidate)
- def candidates_done(self, media):
- "Callback for the network object."
- self.streams[media].candidates_done()
- def codecs(self, media, codecs):
- "Callback for the network object."
- self.streams[media].codecs(codecs)
- def send_local_codecs(self):
- "Callback for the network object."
- for id in self.streams:
- self.streams[id].send_local_codecs()
-
- def make_widget(self):
- "Make the widget of the participant's video stream."
- gtk.gdk.threads_enter()
- self.glade = gtk.glade.XML(gladefile, "user_frame")
- self.userframe = self.glade.get_widget("user_frame")
- self.glade.get_widget("frame_label").set_text(self.cname)
- self.glade.signal_autoconnect(self)
- self.label = gtk.Label()
- self.label.set_alignment(0,0)
- self.label.show()
- self.mainui.hbox_add(self.userframe, self.label)
- gtk.gdk.threads_leave()
-
- def exposed(self, widget, *args):
- """From the exposed signal, used to create the video sink
- The video sink will be created here, but will only be linked when the
- pad arrives and link_video_sink() is called.
- """
- if not VIDEO:
- return
- try:
- self.videosink.get_by_interface(gst.interfaces.XOverlay).expose()
- except AttributeError:
- try:
- self.outcv.acquire()
- self.videosink = make_video_sink(self.pipeline.pipeline,
- widget.window.xid,
- "uservideosink")
- self.pipeline.pipeline.add(self.videosink)
- self.funnel = gst.element_factory_make("fsfunnel")
- self.pipeline.pipeline.add(self.funnel)
- self.funnel.link(self.videosink)
- self.havesize = self.videosink.get_pad("sink").add_buffer_probe(self.have_size)
-
- self.videosink.set_state(gst.STATE_PLAYING)
- self.funnel.set_state(gst.STATE_PLAYING)
- self.outcv.notifyAll()
- finally:
- self.outcv.release()
-
-
- def have_size(self, pad, buffer):
- "Callback on the first buffer to know the drawingarea size"
- x = buffer.caps[0]["width"]
- y = buffer.caps[0]["height"]
- gtk.gdk.threads_enter()
- self.glade.get_widget("user_drawingarea").set_size_request(x,y)
- gtk.gdk.threads_leave()
- self.videosink.get_pad("sink").remove_buffer_probe(self.havesize)
- del self.havesize
- return True
-
-
-
- def link_video_sink(self, pad):
- """Link the video sink
-
- Wait for the funnnel for the video sink to be created, when it has been
- created, link it.
- """
- try:
- self.outcv.acquire()
- while self.funnel is None:
- self.outcv.wait()
- print >>sys.stderr, "LINKING VIDEO SINK"
- pad.link(self.funnel.get_pad("sink%d"))
- finally:
- self.outcv.release()
-
- def destroy(self):
- if VIDEO:
- try:
- self.videosink.get_pad("sink").disconnect_handler(self.havesize)
- pass
- except AttributeError:
- pass
- self.glade.get_widget("user_drawingarea").disconnect_by_func(self.exposed)
- self.streams = {}
- self.outcv.acquire()
- self.videosink.set_locked_state(True)
- self.funnel.set_locked_state(True)
- self.videosink.set_state(gst.STATE_NULL)
- self.funnel.set_state(gst.STATE_NULL)
- self.pipeline.pipeline.remove(self.videosink)
- self.pipeline.pipeline.remove(self.funnel)
- del self.videosink
- del self.funnel
- self.outcv.release()
- gtk.gdk.threads_enter()
- self.userframe.destroy()
- self.label.destroy()
- gtk.gdk.threads_leave()
-
- def error(self):
- "Callback for the network object."
- if self.id == 1:
- self.mainui.fatal_error("<b>Disconnected from server</b>")
- else:
- print "ERROR ON %d" % (self.id)
-
- def recv_codecs_changed(self):
- codecs = {}
- for s in self.streams:
- codec = self.streams[s].fsstream.get_property("current-recv-codecs")
- mediatype = self.streams[s].session.fssession.get_property("media-type")
- if len(codec):
- if mediatype in codecs:
- codecs[mediatype] += codec
- else:
- codecs[mediatype] = codec
- str = ""
- for mt in codecs:
- str += "<big>" +mt.value_nick.title() + "</big>:\n"
- for c in codecs[mt]:
- str += " <b>%s</b>: %s %s\n" % (c.id,
- c.encoding_name,
- c.clock_rate)
- self.label.set_markup(str)
-
- def send_codecs_to(self, participant):
- for sid in self.streams:
- self.streams[sid].send_codecs_to(participant)
-
-
-class FsMainUI:
- "The main UI and its different callbacks"
-
- def __init__(self, mode, ip, port):
- self.mode = mode
- self.pipeline = FsUIPipeline()
- self.pipeline.codecs_changed_audio = self.reset_audio_codecs
- self.pipeline.codecs_changed_video = self.reset_video_codecs
- self.glade = gtk.glade.XML(gladefile, "main_window")
- self.glade.signal_autoconnect(self)
- self.mainwindow = self.glade.get_widget("main_window")
- self.audio_combobox = self.glade.get_widget("audio_combobox")
- self.video_combobox = self.glade.get_widget("video_combobox")
- liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
- self.audio_combobox.set_model(liststore)
- cell = gtk.CellRendererText()
- self.audio_combobox.pack_start(cell, True)
- self.audio_combobox.add_attribute(cell, 'text', 0)
- self.reset_audio_codecs()
- liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT)
- self.video_combobox.set_model(liststore)
- cell = gtk.CellRendererText()
- self.video_combobox.pack_start(cell, True)
- self.video_combobox.add_attribute(cell, 'text', 0)
- self.reset_video_codecs()
-
- if mode == CLIENT:
- self.client = FsUIClient(ip, port, mycname, FsUIParticipant,
- self.pipeline, self)
- self.glade.get_widget("info_label").set_markup(
- "<b>%s</b>\nConnected to %s:%s" % (mycname, ip, port))
- elif mode == SERVER:
- self.server = FsUIListener(port, FsUIServer, mycname,
- FsUIParticipant, self.pipeline, self)
- self.glade.get_widget("info_label").set_markup(
- "<b>%s</b>\nExpecting connections on port %s" %
- (mycname, self.server.port))
-
-
- self.mainwindow.show()
-
- def reset_codecs(self, combobox, fssession):
- liststore = combobox.get_model()
- current = fssession.get_property("current-send-codec")
- liststore.clear()
- for c in fssession.get_property("codecs"):
- str = ("%s: %s/%s %s" % (c.id,
- c.media_type.value_nick,
- c.encoding_name,
- c.clock_rate))
- iter = liststore.append([str, c])
- if current and c and current.id == c.id:
- combobox.set_active_iter(iter)
- print "active: "+ c.to_string()
-
- def reset_audio_codecs(self):
- if AUDIO:
- self.reset_codecs(self.audio_combobox,
- self.pipeline.audiosession.fssession)
-
- def reset_video_codecs(self):
- if VIDEO:
- self.reset_codecs(self.video_combobox,
- self.pipeline.videosession.fssession)
-
- def combobox_changed_cb(self, combobox, fssession):
- liststore = combobox.get_model()
- iter = combobox.get_active_iter()
- if iter:
- codec = liststore.get_value(iter, 1)
- fssession.set_send_codec(codec)
-
- def audio_combobox_changed_cb(self, combobox):
- self.combobox_changed_cb(combobox, self.pipeline.audiosession.fssession)
-
- def video_combobox_changed_cb(self, combobox):
- self.combobox_changed_cb(combobox, self.pipeline.videosession.fssession)
-
-
- def exposed(self, widget, *args):
- "Callback from the exposed event of the widget to make the preview sink"
- if not VIDEO:
- return
- try:
- self.preview.get_by_interface(gst.interfaces.XOverlay).expose()
- except AttributeError:
- self.preview = self.pipeline.make_video_preview(widget.window.xid,
- self.newsize)
-
- def newsize (self, x, y):
- self.glade.get_widget("preview_drawingarea").set_size_request(x,y)
-
- def shutdown(self, widget=None):
- gtk.main_quit()
-
- def hbox_add(self, widget, label):
- table = self.glade.get_widget("users_table")
- x = table.get_properties("n-columns")[0]
- table.attach(widget, x, x+1, 0, 1)
- table.attach(label, x, x+1, 1, 3, xpadding=6)
-
- def __del__(self):
- self.mainwindow.destroy()
-
- def fatal_error(self, errormsg):
- gtk.gdk.threads_enter()
- dialog = gtk.MessageDialog(self.mainwindow,
- gtk.DIALOG_MODAL,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK)
- dialog.set_markup(errormsg);
- dialog.run()
- dialog.destroy()
- gtk.main_quit()
- gtk.gdk.threads_leave()
-
- def show_dtmf(self, button):
- try:
- self.dtmf.present()
- except AttributeError:
- self.dtmf = gtk.glade.XML(gladefile, "dtmf_window")
- self.dtmf.signal_autoconnect(self)
-
- def dtmf_start(self, button):
- if (self.dtmf.get_widget("dtmf_as_event").get_active()):
- self.dtmf_last_method = farsight.DTMF_METHOD_RTP_RFC4733
- elif (self.dtmf.get_widget("dtmf_as_sound").get_active()):
- self.dtmf_last_method = farsight.DTMF_METHOD_IN_BAND
- else:
- print "Invalid DTMF Method"
- return
- self.pipeline.audiosession.dtmf_start(button.get_label(), \
- self.dtmf_last_method)
-
- def dtmf_stop(self, button):
- try:
- self.pipeline.audiosession.dtmf_stop(self.dtmf_last_method)
- del self.dtmf_last_method
- except AttributeError:
- pass
- def dtmf_destroy(self, button):
- self.dtmf.get_widget("dtmf_window").destroy()
- del self.dtmf
-
-
-
-class FsUIStartup:
- "Displays the startup window and then creates the FsMainUI"
-
- def __init__(self):
- self.glade = gtk.glade.XML(gladefile, "neworconnect_dialog")
- self.dialog = self.glade.get_widget("neworconnect_dialog")
- self.glade.get_widget("newport_spinbutton").set_value(9893)
- self.glade.signal_autoconnect(self)
- self.dialog.show()
- self.acted = False
-
- def action(self, mode):
- port = self.glade.get_widget("newport_spinbutton").get_value_as_int()
- ip = self.glade.get_widget("newip_entry").get_text()
- try:
- self.ui = FsMainUI(mode, ip, port)
- self.acted = True
- self.dialog.destroy()
- del self.glade
- del self.dialog
- except socket.error, e:
- dialog = gtk.MessageDialog(self.dialog,
- gtk.DIALOG_MODAL,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK)
- dialog.set_markup("<b>Could not connect to %s %d</b>" % (ip,port))
- dialog.format_secondary_markup(e[1])
- dialog.run()
- dialog.destroy()
-
- def new_server(self, widget):
- self.action(SERVER)
-
- def connect(self, widget):
- self.action(CLIENT)
-
-
- def quit(self, widget):
- if not self.acted:
- gtk.main_quit()
-
-
-
-
-if __name__ == "__main__":
- if len(sys.argv) >= 2:
- CAMERA = sys.argv[1]
- else:
- CAMERA = None
-
- gobject.threads_init()
- gtk.gdk.threads_init()
- startup = FsUIStartup()
- gtk.main()
diff --git a/tests/gui/fs2_gui_net.py b/tests/gui/fs2_gui_net.py
deleted file mode 100644
index c834d77..0000000
--- a/tests/gui/fs2_gui_net.py
+++ /dev/null
@@ -1,456 +0,0 @@
-#!/usr/bin/python
-
-# Farsight 2 simple network signalling library for the demo GUI
-#
-# Copyright (C) 2007 Collabora, Nokia
-# @author: Olivier Crete <olivier.crete at collabora.co.uk>
-#
-# 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
-#
-
-#
-# This is the signalling code used by fs2-gui.py
-#
-
-import sys, os, pwd, os.path
-import socket, struct
-import gc
-
-
-try:
- import pygst
- pygst.require('0.10')
-
- import gst
-except ImportError, e:
- raise SystemExit("Gst-Python couldn't be found! (%s)" % (e[0]))
-
-try:
- import farsight
-except:
- sys.path.append(os.path.join(os.path.dirname(__file__),
- '..', '..', 'python', '.libs'))
- import farsight
-
-import gobject
-
-class FsUIConnect:
- ERROR = 0
- CODECS = 1
- CANDIDATE = 2
- CANDIDATES_DONE = 3
- INTRO = 4
-
- def __reset(self):
- self.type = None
- self.media = None
- self.size = struct.calcsize("!IIIIII")
- self.data = ""
- self.dest = -1
- self.src = -1
-
-
- def __init__(self, sock, callbacks, myid=0):
- self.sock = sock
- self.__reset()
- self.callbacks = callbacks
- self.myid = myid
- self.partid = 1
- self.is_server = True
- sock.setblocking(0)
- gobject.io_add_watch(self.sock.fileno(), gobject.IO_IN,
- self.__data_in)
- gobject.io_add_watch(self.sock.fileno(),
- gobject.IO_ERR | gobject.IO_HUP,
- self.__error)
-
- def __error(self, source, condition):
- print "have error"
- if (self.src >= 0):
- self.callbacks[self.ERROR](self.src)
- else:
- self.callbacks[self.ERROR](self.partid)
- return False
-
- def __data_in(self, source, condition):
- data = self.sock.recv(self.size-len(self.data))
-
- if len(data) == 0:
- print "received nothing"
- if (self.src >= 0):
- self.callbacks[self.ERROR](self.src)
- else:
- self.callbacks[self.ERROR](self.partid)
- return False
-
- self.data += data
- if len(self.data) == self.size:
- if self.type is not None:
- if self.type == self.CODECS:
- data = self.__codecs_from_string(data)
- elif self.type == self.CANDIDATE:
- data = self.__candidate_from_string(data)
- else:
- data = self.data
- self.callbacks[self.type](self.src, self.dest,
- self.media, data)
- self.__reset()
- else:
- (check,
- self.src,
- self.dest,
- self.type,
- self.media,
- self.size) = struct.unpack("!IIIIII", self.data)
- if check != 0xDEADBEEF:
- print "CORRUPTION"
- sys.exit(1)
- if self.myid > 1 and self.dest != self.myid:
- print "GOT MESSAGE FOR %d, but I am %d" % (self.dest,
- self.myid)
- sys.exit(1)
- self.data=""
- if self.size == 0:
- self.callbacks[self.type](self.src, self.dest,
- self.media, None)
- self.__reset()
- return True
-
- def __send_data(self, dest, type, media=0, data="", src=None):
- if src is None: src = self.myid
- if src == 0 and type != self.INTRO: raise Exception
- try:
- self.sock.sendall(struct.pack("!IIIIII",
- 0xDEADBEEF,
- int(src),
- int(dest),
- int(type),
- int(media),
- len(data)))
- self.sock.sendall(data)
- except socket.error:
- print "have error"
- self.callbacks[self.ERROR](self.partid)
-
-
- def send_error(self, dest, src):
- self.__send_data(dest, self.ERROR, src=src)
- def send_intro(self, dest, cname, src=None):
- self.__send_data(dest, self.INTRO, data=cname, src=src)
- def send_codecs(self, dest, media, codecs, src=None):
- self.__send_data(dest, self.CODECS,
- media=media,
- data=self.__codecs_to_string(codecs), src=src)
- def send_candidate(self, dest, media, candidate, src=None):
- self.__send_data(dest, self.CANDIDATE, media=media,
- data=self.__candidate_to_string(candidate), src=src)
- def send_candidates_done(self, dest, media, src=None):
- self.__send_data(dest, self.CANDIDATES_DONE, media=media, src=src)
-
- def __del__(self):
- try:
- self.sock.close()
- except AttributeError:
- pass
-
-
- def __candidate_to_string(self, candidate):
- return "|".join((
- candidate.foundation,
- str(candidate.component_id),
- candidate.ip,
- str(candidate.port),
- candidate.base_ip,
- str(candidate.base_port),
- str(int(candidate.proto)),
- str(candidate.priority),
- str(int(candidate.type)),
- candidate.username,
- candidate.password))
-
- def __candidate_from_string(self, string):
- candidate = farsight.Candidate()
- (candidate.foundation,
- component_id,
- candidate.ip,
- port,
- candidate.base_ip,
- base_port,
- proto,
- priority,
- type,
- candidate.username,
- candidate.password) = string.split("|")
- candidate.component_id = int(component_id)
- candidate.port = int(port)
- candidate.base_port = int(base_port)
- candidate.proto = int(proto)
- candidate.priority = int(priority)
- candidate.type = int(type)
- return candidate
-
- def __codecs_to_string(self, codecs):
- codec_strings = []
- for codec in codecs:
- start = " ".join((str(codec.id),
- codec.encoding_name,
- str(int(codec.media_type)),
- str(codec.clock_rate),
- str(codec.channels)))
- codec = "".join((start,
- "|",
- ";".join(["=".join(i) for i in codec.optional_params])))
- codec_strings.append(codec)
-
- return "\n".join(codec_strings)
-
-
- def __codecs_from_string(self, string):
- codecs = []
- for substring in string.split("\n"):
- (start,end) = substring.split("|")
- (id, encoding_name, media_type, clock_rate, channels) = start.split(" ")
- codec = farsight.Codec(int(id), encoding_name, int(media_type),
- int(clock_rate))
- codec.channels = int(channels)
- if len(end):
- codec.optional_params = \
- [tuple(x.split("=",1)) for x in end.split(";") if len(x) > 0]
- codecs.append(codec)
- return codecs
-
-class FsUIConnectClient (FsUIConnect):
- def __init__(self, ip, port, callbacks):
- sock = socket.socket()
- sock.connect((ip, port))
- FsUIConnect.__init__(self, sock, callbacks)
- self.is_server = False
-
-class FsUIListener:
- def __init__(self, port, callback, *args):
- self.sock = socket.socket()
- self.callback = callback
- self.args = args
- bound = False
- while not bound:
- try:
- self.sock.bind(("", port))
- bound = True
- except socket.error, e:
- port += 1
- self.port = port
- print "Bound to port ", port
- self.sock.setblocking(0)
- gobject.io_add_watch(self.sock.fileno(), gobject.IO_IN, self.data_in)
- gobject.io_add_watch(self.sock.fileno(),
- gobject.IO_ERR | gobject.IO_HUP,
- self.error)
- self.sock.listen(3)
-
- def error(self, source, condition):
- print "Error on listen"
- sys.exit(1)
- return False
-
- def data_in(self, source, condition):
- (sock,addr) = self.sock.accept()
- self.callback(sock, *self.args)
- return True
-
-class FsUIClient:
- def __init__(self, ip, port, cname, get_participant, *args):
- self.participants = {}
- self.get_participant = get_participant
- self.args = args
- self.cname = cname
- self.connect = FsUIConnectClient(ip, port, (self.__error,
- self.__codecs,
- self.__candidate,
- self.__candidate_done,
- self.__intro))
- self.connect.send_intro(1, cname)
-
- def __codecs(self, src, dest, media, data):
- print "Got codec Src:%d dest:%d data:%s" % (src, dest, data)
- self.participants[src].codecs(media, data)
- def __candidate(self, src, dest, media, data):
- self.participants[src].candidate(media, data)
- def __candidate_done(self, src, dest, media, data):
- self.participants[src].candidates_done(media)
- def __intro(self, src, dest, media, cname):
- print "Got Intro from %s, I am %d" % (src, dest)
- if src == 1:
- self.connect.myid = dest
- if not self.participants.has_key(src):
- if src != 1:
- self.connect.send_intro(src, self.cname)
- self.participants[src] = self.get_participant(self.connect, src,
- cname,
- *self.args)
- def __error(self, participantid, *arg):
- print "Client Error", participantid
- if participantid == 1:
- # Communication error with server, its over
- self.participants[participantid].error()
- else:
- self.participants[participantid].destroy()
- del self.participants[participantid]
- gc.collect()
-
-
-class FsUIServer:
- nextid = 2
- participants = {}
-
- def __init__(self, sock, cname, get_participant, *args):
- self.cname = cname
- self.get_participant = get_participant
- self.args = args
- self.connect = FsUIConnect(sock, (self.__error,
- self.__codecs,
- self.__candidate,
- self.__candidate_done,
- self.__intro), 1)
- def __codecs(self, src, dest, media, data):
- FsUIServer.participants[src].codecs(media, data)
- def __candidate(self, src, dest, media, data):
- if dest == 1:
- FsUIServer.participants[src].candidate(media, data)
- else:
- print data
- FsUIServer.participants[dest].connect.send_candidate(dest,
- media,
- data,
- src)
- def __candidate_done(self, src, dest, media, data):
- if dest == 1:
- FsUIServer.participants[src].candidates_done(media)
- else:
- FsUIServer.participants[dest].connect.send_candidates_done(dest,
- media,
- src)
- def __intro(self, src, dest, media, cname):
- print "Got Intro from %s to %s" % (src, dest)
- if src == 0 and dest == 1:
- newid = FsUIServer.nextid
- # Forward the introduction to all other participants
- for pid in FsUIServer.participants:
- print "Sending from %d to %d" % (newid, pid)
- FsUIServer.participants[pid].connect.send_intro(pid, cname,
- newid)
- self.connect.send_intro(newid, self.cname)
- self.connect.partid = newid
- FsUIServer.participants[newid] = self.get_participant(self.connect,
- newid,
- cname,
- *self.args)
- FsUIServer.participants[newid].send_local_codecs()
- FsUIServer.nextid += 1
- elif dest != 1:
- FsUIServer.participants[dest].connect.send_intro(dest,
- cname,
- src)
- FsUIServer.participants[src].send_codecs_to(
- FsUIServer.participants[dest])
- else:
- print "ERROR SRC != 0"
-
- def __error(self, participantid, *args):
- print "Server Error", participantid
- FsUIServer.participants[participantid].destroy()
- del FsUIServer.participants[participantid]
- gc.collect()
- for pid in FsUIServer.participants:
- FsUIServer.participants[pid].connect.send_error(pid, participantid)
-
-if __name__ == "__main__":
- class TestMedia:
- def __init__(self, pid, id, connect):
- self.pid = pid
- self.id = id
- self.connect = connect
- candidate = farsight.Candidate()
- candidate.component_id = 1
- connect.send_candidate(self.pid, self.id, candidate)
- connect.send_candidates_done(self.pid, self.id)
- def candidate(self, candidate):
- print "Got candidate", candidate
- def candidates_done(self):
- print "Got candidate done"
- def codecs(self, codecs):
- if self.connect.myid != 1:
- self.connect.send_codecs(1, self.id,
- [farsight.Codec(self.connect.myid,
- "codec-name",
- self.pid,
- self.id)])
-
- def send_local_codecs(self):
- print "Send local codecs to %s for media %s" % (self.pid, self.id)
- self.connect.send_codecs(self.pid, self.id,
- [farsight.Codec(self.connect.myid,
- "local_codec",
- self.pid,
- self.id)])
- def get_codecs(self):
- return [farsight.Codec(self.connect.myid,
- "nego-codecs",
- self.pid,
- self.id)]
-
-
- class TestParticipant:
- def __init__(self, connect, id, cname, *args):
- self.id = id
- self.streams = {1: TestMedia(id, 1, connect),
- 2: TestMedia(id, 2, connect)}
- self.cname = cname
- self.connect = connect
- print "New Participant %s and cname %s" % (id,cname)
- def candidate(self, media, candidate):
- self.streams[media].candidate(candidate)
- def candidates_done(self, media):
- self.streams[media].candidates_done()
- def codecs(self, media, codecs):
- self.streams[media].codecs(codecs)
- def send_local_codecs(self):
- for id in self.streams:
- self.streams[id].send_local_codecs()
- def destroy(self):
- pass
- def send_codecs_to(self, participant):
- for sid in self.streams:
- print "to: %s from: %s" % (str(participant.id), (self.id))
- participant.connect.send_codecs(participant.id,
- self.streams[sid].id,
- self.streams[sid].get_codecs(),
- self.id)
- def error(self):
- print "ERROR"
- sys.exit(1)
- def destroy(self):
- passs
-
-
- mycname = "test"
- mainloop = gobject.MainLoop()
- gobject.threads_init()
- if len(sys.argv) > 1:
- client = FsUIClient("127.0.0.1", int(sys.argv[1]),
- "cname" + sys.argv[1],
- TestParticipant)
- else:
- listener = FsUIListener(9893, FsUIServer, "cnameServ", TestParticipant)
- mainloop.run()
--
1.5.6.5
More information about the farsight-commits
mailing list