[farsight2/master] Add python gui demo program

Olivier Crête olivier.crete at collabora.co.uk
Tue Dec 23 15:20:36 PST 2008


---
 tests/gui/fs2-gui.glade  |  352 +++++++++++++++++++++++++++++++++++
 tests/gui/fs2-gui.py     |  463 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/gui/fs2_gui_net.py |  366 ++++++++++++++++++++++++++++++++++++
 3 files changed, 1181 insertions(+), 0 deletions(-)
 create mode 100644 tests/gui/fs2-gui.glade
 create mode 100644 tests/gui/fs2-gui.py
 create mode 100644 tests/gui/fs2_gui_net.py

diff --git a/tests/gui/fs2-gui.glade b/tests/gui/fs2-gui.glade
new file mode 100644
index 0000000..8c8b69f
--- /dev/null
+++ b/tests/gui/fs2-gui.glade
@@ -0,0 +1,352 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.0 on Mon Dec 17 16:35:38 2007 -->
+<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>
+    <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>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="user_hbox">
+            <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="GtkFrame" id="user_frame2">
+                <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_xalign">0</property>
+                <property name="shadow_type">GTK_SHADOW_IN</property>
+                <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>
+                    <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">1</property>
+                        <property name="n_columns">1</property>
+                        <child>
+                          <widget class="GtkDrawingArea" id="preview_drawingarea">
+                            <property name="width_request">320</property>
+                            <property name="height_request">200</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"/>
+                            <signal name="realize" handler="drawing_realize"/>
+                          </widget>
+                          <packing>
+                            <property name="x_padding">6</property>
+                            <property name="y_padding">6</property>
+                          </packing>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+                <child>
+                  <widget class="GtkLabel" id="frame_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">&lt;b&gt;My preview&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </widget>
+                  <packing>
+                    <property name="type">label_item</property>
+                  </packing>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkHBox" id="participants_hbox">
+                <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="position">1</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_END</property>
+            <child>
+              <widget class="GtkButton" id="button2">
+                <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-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>
+              </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="GtkFrame" 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="label_xalign">0</property>
+        <property name="shadow_type">GTK_SHADOW_IN</property>
+        <child>
+          <widget class="GtkTable" id="table5">
+            <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">1</property>
+            <property name="n_columns">1</property>
+            <property name="row_spacing">1</property>
+            <child>
+              <widget class="GtkDrawingArea" id="user_drawingarea">
+                <property name="width_request">320</property>
+                <property name="height_request">200</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"/>
+                <signal name="realize" handler="drawing_realize"/>
+              </widget>
+              <packing>
+                <property name="x_padding">6</property>
+                <property name="y_padding">6</property>
+              </packing>
+            </child>
+          </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">&lt;b&gt;User&lt;/b&gt;</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="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">2</property>
+            <property name="column_spacing">12</property>
+            <property name="row_spacing">12</property>
+            <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">9393 1 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>
+              </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">2</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="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="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">2</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="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>
+          </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>
+</glade-interface>
diff --git a/tests/gui/fs2-gui.py b/tests/gui/fs2-gui.py
new file mode 100644
index 0000000..182a9fc
--- /dev/null
+++ b/tests/gui/fs2-gui.py
@@ -0,0 +1,463 @@
+#!/usr/bin/python
+
+import sys, os, pwd, os.path
+import socket, struct
+import threading
+
+try:
+    import pygtk
+    pygtk.require("2.0")
+
+    import gtk, gtk.glade, gobject, gtk.gdk
+
+    import gobject
+    gobject.threads_init()
+except ImportError:
+    raise SystemExit("PyGTK couldn't be found !")
+
+try:
+    import pygst
+    pygst.require('0.10')
+        
+    import gst
+except ImportError:
+    raise SystemExit("Gst-Python couldn't be found!")
+try:
+    import farsight
+except:
+    sys.path.append("../../python/.libs")
+    import farsight
+
+
+from fs2_gui_net import  FsUIClient, FsUIListener, FsUIServer
+
+CAMERA=False
+
+CLIENT=1
+SERVER=2
+
+TRANSMITTER="rawudp"
+
+mycname = "".join((pwd.getpwuid(os.getuid())[0],
+                   "-" ,
+                   str(os.getpid()),
+                   "@",
+                   socket.gethostname()))
+
+
+def make_video_sink(pipeline, xid):
+    bin = gst.Bin("videosink_%d" % xid)
+#    sink = gst.element_factory_make("autovideosink", "realsink")
+#    sink.connect("element-added",
+#                 lambda bin, element: element.set_property("sync", False))
+#    sink.connect("element-added",
+#                 lambda bin, element: element.set_property("async", False))
+    sink = gst.element_factory_make("ximagesink", "realsink")
+#    sink.set_property("async", False)
+    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:
+    
+    def __init__(self, elementname="fsrtpconference"):
+        self.pipeline = gst.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)
+        self.conf.set_property("sdes-cname", mycname)
+        self.pipeline.add(self.conf)
+#        self.audiosource = FsUIAudioSource(self.pipeline)
+        self.videosource = FsUIVideoSource(self.pipeline)
+#        self.audiosession = FsUISession(self.conf, self.audiosource)
+        self.videosession = FsUISession(self.conf, self.videosource)
+        self.adder = gst.element_factory_make("adder")
+#        self.audiosink = gst.element_factory_make("alsasink")
+#        self.pipeline.add(self.audiosink)
+#        self.pipeline.add(self.adder)
+#        self.adder.link(self.audiosink)
+        self.pipeline.set_state(gst.STATE_PLAYING)
+        self.participants = []
+
+
+    def __del__(self):
+        self.pipeline.set_state(gst.STATE_NULL)
+
+    def sync_handler(self, bus, message):
+        if message.type == gst.MESSAGE_ELEMENT and \
+               message.structure.has_name("prepare-xwindow-id"):
+            xid = None
+            element = message.src
+            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):
+        if message.type != gst.MESSAGE_STATE_CHANGED:
+            print message.type
+        if message.type == gst.MESSAGE_ERROR:
+            print message.parse_error()
+            message.src.set_state(gst.STATE_NULL)
+            message.src.set_state(gst.STATE_PLAYING)
+        elif message.type == gst.MESSAGE_WARNING:
+            print message.parse_warning()
+        
+        return True
+
+    def make_video_preview(self, xid):
+        self.previewsink = make_video_sink(self.pipeline, xid)
+        self.pipeline.add(self.previewsink)
+        self.previewsink.set_state(gst.STATE_PLAYING)
+        self.videosource.tee.link(self.previewsink)
+        self.pipeline.set_state(gst.STATE_PLAYING)
+        return self.previewsink
+
+    def add_user(self, ip, port):
+        self.intro(FsUIConnectClient (ip, port))
+
+    def intro(conn):
+        self.participants.append(FsUIParticipant(conn),
+                                 None,
+#                                 self.audiosession,
+                                 self.videosession)
+
+
+class FsUISource:
+    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()
+#        self.source.set_locked_state(1)
+        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):
+        raise NotImplementedError()
+
+
+    def get_type(self):
+        raise NotImplementedError()
+
+    def get_src_pad(self, name="src%d"):
+        return self.tee.get_request_pad(name)
+
+    def put_src_pad(self, pad):
+        self.tee.release_request_pad(pad)
+    
+
+class FsUIVideoSource(FsUISource):
+    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)
+            identity = gst.element_factory_make("identity")
+            identity.set_property("sync", True)
+            bin.add(identity)
+            source.link(identity)
+            source = identity
+            
+        colorspace = gst.element_factory_make("ffmpegcolorspace")
+        bin.add(colorspace)
+        videoscale = gst.element_factory_make("videoscale")
+        bin.add(videoscale)
+        source.link(colorspace)
+        colorspace.link(videoscale)
+        capsfilter = gst.element_factory_make("capsfilter")
+        bin.add(capsfilter)
+        capsfilter.set_property("caps",gst.caps_from_string("video/x-raw-yuv, format=(fourcc)I420, width=352, height=288, framerate=15/1"))
+        videoscale.link(capsfilter)
+        bin.add_pad(gst.GhostPad("src", capsfilter.get_pad("src")))
+        return bin
+            
+      
+
+class FsUIAudioSource(FsUISource): 
+    def get_type(self):
+        return farsight.MEDIA_TYPE_AUDIO
+
+    def make_source(self):
+        return gst.element_factory_make("alsasrc")
+
+
+
+class FsUISession:
+    def __init__(self, conference, source):
+        self.conference = conference
+        self.source = source
+        self.streams = []
+        self.session = conference.new_session(source.get_type())
+        if source.get_type() == farsight.MEDIA_TYPE_VIDEO:
+            self.session.set_property("local-codecs-config",
+                                      [farsight.Codec(farsight.CODEC_ID_ANY,
+                                                      "H263",
+                                                      farsight.MEDIA_TYPE_VIDEO,
+                                                      0),
+                                       farsight.Codec(farsight.CODEC_ID_DISABLE,
+                                                      "H264",
+                                                      farsight.MEDIA_TYPE_VIDEO,
+                                                      0)])
+        self.session.connect("new-negotiated-codecs",
+                             self.__new_negotiated_codecs)
+        self.sourcepad = self.source.get_src_pad()
+        self.sourcepad.link(self.session.get_property("sink-pad"))
+
+    def __del__(self):
+        self.sourcepad(unlink)
+        self.source.put_src_pad(self.sourcepad)
+        
+    def __new_negotiated_codecs(self, session):
+        for s in self.streams:
+            s.new_negotiated_codecs()
+            
+    def new_stream(self, id, connect, participant):
+        realstream = self.session.new_stream(participant.participant,
+                                             farsight.DIRECTION_BOTH,
+                                             TRANSMITTER)
+        stream = FsUIStream(id, connect, self, participant, realstream)
+        self.streams.append(stream)
+        return stream
+    
+
+class FsUIStream:
+    def __init__(self, id, connect, session, participant, stream):
+        self.id = id
+        self.session = session
+        self.participant = participant
+        self.stream = stream
+        self.connect = connect
+        self.stream.connect("local-candidates-prepared",
+                            self.__local_candidates_prepared)
+        self.stream.connect("new-local-candidate",
+                            self.__new_local_candidate)
+        self.stream.connect("src-pad-added", self.__src_pad_added)
+        self.newcodecs = []
+        
+    def __local_candidates_prepared(self, streem):
+        self.connect.send_candidates_done(self.participant.id, self.id)
+    def __new_local_candidate(self, stream, candidate):
+        self.connect.send_candidate(self.participant.id, self.id, candidate)
+    def __src_pad_added(self, stream, pad, codec):
+        self.participant.link_sink(pad)
+    def candidate(self, candidate):
+        self.stream.add_remote_candidate(candidate)
+    def candidates_done(self):
+        self.stream.remote_candidates_added()
+    def codec(self, codec):
+        self.newcodecs.append(codec)
+    def codecs_done(self):
+        if len(self.newcodecs) > 0:
+            self.codecs = self.newcodecs
+            self.newcodecs = []
+        try:
+            self.stream.set_remote_codecs(self.codecs)
+        except AttributeError:
+            print "tried to set codecs with 0 codec"
+    def send_local_codecs(self):
+        codecs = self.session.session.get_property("negotiated-codecs")
+        if codecs is None or len(codecs) == 0:
+            codecs = self.session.session.get_property("local-codecs")
+        for codec in codecs:
+            self.connect.send_codec(self.participant.id, self.id, codec)
+        self.connect.send_codecs_done(self.participant.id, self.id)
+    def new_negotiated_codecs(self):
+        if self.participant.id == 1 or self.connect.myid == 1:
+            for codec in self.session.session.get_property("negotiated-codecs"):
+                self.connect.send_codec(self.participant.id, self.id, codec)
+            self.connect.send_codecs_done(self.participant.id, self.id)
+
+
+class FsUIParticipant:
+    def __init__(self, connect, id, cname, pipeline, mainui):
+        self.connect = connect
+        self.id = id
+        self.cname = cname
+        self.pipeline = pipeline
+        self.mainui = mainui
+        self.participant = pipeline.conf.new_participant(cname)
+        self.outcv = threading.Condition()
+        self.funnel = None
+        self.make_widget()
+        self.streams = {
+#            int(farsight.MEDIA_TYPE_AUDIO):
+#                             pipeline.audiosession.new_stream(
+#                                  int(farsight.MEDIA_TYPE_AUDIO),
+#                                  connect, self),
+                        int(farsight.MEDIA_TYPE_VIDEO):
+                             pipeline.videosession.new_stream(
+                                  int(farsight.MEDIA_TYPE_VIDEO),
+                                  connect, self)}
+        
+    def candidate(self, media, candidate):
+        self.streams[media].candidate(candidate)
+    def candidates_done(self, media):
+        self.streams[media].candidates_done()
+    def codec(self, media, codec):
+        self.streams[media].codec(codec)
+    def codecs_done(self, media):
+        self.streams[media].codecs_done()
+    def send_local_codecs(self):
+        for id in self.streams:
+            self.streams[id].send_local_codecs()
+    def make_widget(self):
+        self.glade = gtk.glade.XML("fs2-gui.glade", "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.mainui.hbox_add(self.userframe)
+        
+    def exposed(self, widget, *args):
+        try:
+            self.videosink.get_by_name("realsink").expose()
+        except AttributeError:
+            try:
+                self.outcv.acquire()
+                self.videosink = make_video_sink(self.pipeline.pipeline,
+                                                 widget.window.xid)
+                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.videosink.set_state(gst.STATE_PLAYING)
+                self.funnel.set_state(gst.STATE_PLAYING)
+                self.outcv.notifyAll()
+            finally:
+                self.outcv.release()
+            
+
+
+    def link_sink(self, pad):
+        try:
+            self.outcv.acquire()
+            while self.funnel is None:
+                self.outcv.wait()
+            topad.link(self.funnel.get_pad("sink%d"))
+        finally:
+            self.outcv.release()
+
+    
+
+class FsMainUI:
+    
+    def __init__(self, mode, ip, port):
+        self.mode = mode
+        self.pipeline = FsUIPipeline()
+        self.glade = gtk.glade.XML("fs2-gui.glade", "main_window")
+        self.glade.signal_autoconnect(self)
+        self.mainwindow = self.glade.get_widget("main_window")
+        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 exposed(self, widget, *args):
+        try:
+            self.preview.get_by_name("realsink").expose()
+        except AttributeError:
+            print "errored"
+            self.preview = self.pipeline.make_video_preview(widget.window.xid)
+
+    def shutdown(self, widget=None):
+        mainloop.quit()
+        
+    def hbox_add(self, widget):
+        self.glade.get_widget("user_hbox").pack_start(widget, True, True, 0)
+
+    def __del__(self):
+        self.mainwindow.destroy()
+
+class FsUIStartup:
+    def __init__(self):
+        self.glade = gtk.glade.XML("fs2-gui.glade", "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:
+            mainloop.quit()
+
+
+
+
+if __name__ == "__main__":
+    if len(sys.argv) >= 2:
+        CAMERA = sys.argv[1]
+    else:
+        CAMERA = None
+    
+    mainloop = gobject.MainLoop()
+    gobject.threads_init()
+    startup = FsUIStartup()
+    mainloop.run()
diff --git a/tests/gui/fs2_gui_net.py b/tests/gui/fs2_gui_net.py
new file mode 100644
index 0000000..9fe0a2e
--- /dev/null
+++ b/tests/gui/fs2_gui_net.py
@@ -0,0 +1,366 @@
+#!/usr/bin/python
+
+import sys, os, pwd, os.path
+import socket, struct
+
+try:
+    import farsight
+except:
+    sys.path.append("../../python/.libs")
+    import farsight
+
+import gobject
+
+class FsUIConnect:
+    ERROR = 0
+    CODEC = 1
+    CODECS_DONE = 2
+    CANDIDATE = 3
+    CANDIDATES_DONE = 4
+    INTRO = 5
+
+    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
+        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):
+        self.callbacks[self.ERROR]()
+        return False
+
+    def __data_in(self, source, condition):
+        data = self.sock.recv(self.size-len(self.data))
+        
+        self.data += data
+        if len(self.data) == self.size:
+            if self.type is not None:
+                if self.type == self.CODEC:
+                    data = self.__codec_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"
+                    mainloop.quit()
+                self.data=""
+                if self.size == 0:
+                    self.callbacks[self.type](self.src, self.dest,
+                                              self.media, data)
+                    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
+        self.sock.sendall(struct.pack("!IIIIII",
+                                      0xDEADBEEF,
+                                      int(src),
+                                      int(dest),
+                                      int(type),
+                                      int(media),
+                                      len(data)))
+        self.sock.sendall(data)
+
+
+    def send_intro(self, dest, cname, src=None):
+        self.__send_data(dest, self.INTRO, data=cname, src=src)
+    def send_codec(self, dest, media, codec, src=None):
+        self.__send_data(dest, self.CODEC,
+                         media=media,
+                         data=self.__codec_to_string(codec))
+    def send_codecs_done(self, dest, media):
+        self.__send_data(dest, self.CODECS_DONE, media=media)
+    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.candidate_id,
+            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.candidate_id,
+         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 __codec_to_string(self, codec):
+        start = " ".join((str(codec.id),
+                          codec.encoding_name,
+                          str(int(codec.media_type)),
+                          str(codec.clock_rate),
+                          str(codec.channels)))
+        return "".join((start,
+                       "|",
+                       ":".join(["=".join(i) for i in codec.optional_params])))
+
+
+    def __codec_from_string(self, string):
+        (start,end) = string.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("=")) for x in end.split(":") if len(x) > 0]
+        return codec
+
+class FsUIConnectClient (FsUIConnect):
+    def __init__(self, ip, port, callbacks):
+        sock = socket.socket()
+        sock.connect((ip, port))
+        FsUIConnect.__init__(self, sock, callbacks)
+
+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"
+        mainloop.quit()
+        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.__codec,
+                                                    self.__codecs_done,
+                                                    self.__candidate,
+                                                    self.__candidate_done,
+                                                    self.__intro))
+        self.connect.send_intro(1, cname)
+
+    def __codec(self, src, dest, media, data):
+        self.participants[src].codec(media, data)
+    def __codecs_done(self, src, dest, media, data):
+        self.participants[src].codecs_done(media)
+    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"
+        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, *arg):
+        print "ERROR"
+
+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.__codec,
+                                          self.__codecs_done,
+                                          self.__candidate,
+                                          self.__candidate_done,
+                                          self.__intro), 1)
+    def __codec(self, src, dest, media, data):
+        FsUIServer.participants[src].codec(media, data)
+    def __codecs_done(self, src, dest, media, data):
+        FsUIServer.participants[src].codecs_done(media)
+    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"
+        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)
+            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)
+        else:
+            print "ERROR SRC != 0"
+    def __error(self, *args):
+        print "ERROR"
+
+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 codec(self, codec):
+            print "Got codec src:%d dest:%d media:%d src:%s" % (codec.id, int(codec.media_type), codec.clock_rate, codec.encoding_name)
+        def codecs_done(self):
+            print "Got codecs done from %s for media %s" % (self.pid, self.id)
+            if self.connect.myid != 1:
+                self.connect.send_codec(1, self.id,
+                                        farsight.Codec(self.connect.myid,
+                                                       "codecs_done",
+                                                       self.pid,
+                                                       self.id))
+                self.connect.send_codecs_done(1, self.id)
+        def send_local_codecs(self):
+            print "Send local codecs to %s for media %s" % (self.pid, self.id)
+            self.connect.send_codec(self.pid, self.id,
+                                    farsight.Codec(self.connect.myid,
+                                                   "local_codec",
+                                                   self.pid,
+                                                   self.id))
+            self.connect.send_codecs_done(self.pid, self.id)
+            
+            
+    class TestParticipant:
+        def __init__(self, connect, id, cname, *args):
+            self.id = id
+            self.medias = {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.medias[media].candidate(candidate)
+        def candidates_done(self, media):
+            self.medias[media].candidates_done()
+        def codec(self, media, codec):
+            self.medias[media].codec(codec)
+        def codecs_done(self, media):
+            self.medias[media].codecs_done()
+        def send_local_codecs(self):
+            for id in self.medias:
+                self.medias[id].send_local_codecs()
+            
+
+    mycname = "test"
+    mainloop = gobject.MainLoop()
+    gobject.threads_init()
+    if len(sys.argv) > 1:
+        client = FsUIClient("127.0.0.1", int(sys.argv[1]), TestParticipant)
+    else:
+        listener = FsUIListener(9893, FsUIServer, TestParticipant)
+    mainloop.run()
-- 
1.5.6.5




More information about the farsight-commits mailing list