[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"><b>My preview</b></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"><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="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