r6171 - in flumotion/trunk: . data data/glade flumotion/component/consumers/httpstreamer flumotion/component/misc flumotion/component/misc/cortado flumotion/wizard

flumotion-commit at lists.fluendo.com flumotion-commit at lists.fluendo.com
Wed Feb 6 15:13:29 CET 2008


Author: jdahlin
Date: Wed Feb  6 15:13:25 2008
New Revision: 6171

Log:
2008-02-06  Johan Dahlin  <johan at gnome.org>

	* configure.ac:
	* flumotion/component/misc/cortado/cortado_location.py.in:
	* README:

	Add an optional cortado dependency, store the location and
	update README.
	
	* data/Makefile.am:
	* data/cortado-template.html:
	* flumotion/component/misc/Makefile.am:
	* flumotion/component/misc/cortado/Makefile.am:
	* flumotion/component/misc/cortado/__init__.py:
	* flumotion/component/misc/cortado/cortado.py:
	* flumotion/component/misc/cortado/cortado.xml:

	Create a CortadoPlug which can be plugged into a http streaming
	component.
	
	* data/glade/wizard_http.glade:
	* flumotion/component/consumers/httpstreamer/http_wizard.py:
	* flumotion/wizard/models.py:
	* flumotion/wizard/save.py:

	Add support for streaming to cortado widgets in the admin wizard.

	Fixes #824



Added:
   flumotion/trunk/data/cortado-template.html
   flumotion/trunk/flumotion/component/misc/cortado/   (props changed)
   flumotion/trunk/flumotion/component/misc/cortado/Makefile.am
   flumotion/trunk/flumotion/component/misc/cortado/__init__.py   (contents, props changed)
   flumotion/trunk/flumotion/component/misc/cortado/cortado.py   (contents, props changed)
   flumotion/trunk/flumotion/component/misc/cortado/cortado.xml
   flumotion/trunk/flumotion/component/misc/cortado/cortado_location.py.in
Modified:
   flumotion/trunk/ChangeLog
   flumotion/trunk/README
   flumotion/trunk/configure.ac
   flumotion/trunk/data/Makefile.am
   flumotion/trunk/data/glade/wizard_http.glade
   flumotion/trunk/flumotion/component/consumers/httpstreamer/http_wizard.py
   flumotion/trunk/flumotion/component/misc/Makefile.am
   flumotion/trunk/flumotion/wizard/models.py
   flumotion/trunk/flumotion/wizard/save.py

Modified: flumotion/trunk/ChangeLog
==============================================================================
--- flumotion/trunk/ChangeLog	(original)
+++ flumotion/trunk/ChangeLog	Wed Feb  6 15:13:25 2008
@@ -1,5 +1,32 @@
 2008-02-06  Johan Dahlin  <johan at gnome.org>
 
+	* configure.ac:
+	* flumotion/component/misc/cortado/cortado_location.py.in:
+	* README:
+
+	Add an optional cortado dependency, store the location and
+	update README.
+	
+	* data/Makefile.am:
+	* data/cortado-template.html:
+	* flumotion/component/misc/Makefile.am:
+	* flumotion/component/misc/cortado/Makefile.am:
+	* flumotion/component/misc/cortado/__init__.py:
+	* flumotion/component/misc/cortado/cortado.py:
+	* flumotion/component/misc/cortado/cortado.xml:
+
+	Create a CortadoPlug which can be plugged into a http streaming
+	component.
+	
+	* data/glade/wizard_http.glade:
+	* flumotion/component/consumers/httpstreamer/http_wizard.py:
+	* flumotion/wizard/models.py:
+	* flumotion/wizard/save.py:
+
+	Add support for streaming to cortado widgets in the admin wizard.
+
+2008-02-06  Johan Dahlin  <johan at gnome.org>
+
 	(HTTPFileStreamer._createRootResourceForPath): Remove factory variable
 	to silence pychecker.
 

Modified: flumotion/trunk/README
==============================================================================
--- flumotion/trunk/README	(original)
+++ flumotion/trunk/README	Wed Feb  6 15:13:25 2008
@@ -48,6 +48,10 @@
 
 epydoc
 
+And if you want support for java applets:
+
+cortado
+
 ISSUES
 ------
 

Modified: flumotion/trunk/configure.ac
==============================================================================
--- flumotion/trunk/configure.ac	(original)
+++ flumotion/trunk/configure.ac	Wed Feb  6 15:13:25 2008
@@ -158,6 +158,26 @@
 TWISTED_MODULE([twisted.names])
 TWISTED_MODULE([twisted.web])
 
+dnl Cortado
+AC_SUBST(CORTADO_FILENAME)
+AC_ARG_WITH(cortado_prefix,
+           AC_HELP_STRING(--with-cortado-prefix=<dir>, where cortado can be found))
+
+AC_MSG_CHECKING(for cortado)
+RESULT="no"
+CORTADO_FILENAME=""
+for dir in $with_cortado_prefix /usr/local /usr; do
+    for version in 0.2.2 0.2.1 0.2.0; do
+	filename=$dir/share/cortado/cortado-ovt-stripped-$version.1.jar
+        if test -e "$filename"; then
+	     RESULT="found"
+             CORTADO_FILENAME="$filename"
+ 	     break 2;
+        fi
+    done
+done
+AC_MSG_RESULT($RESULT)
+
 AC_CONFIG_FILES([env], [chmod +x env])
 AC_CONFIG_FILES([bin/flumotion], [chmod +x bin/flumotion])
 AC_CONFIG_FILES([bin/flumotion-admin], [chmod +x bin/flumotion-admin])
@@ -211,6 +231,8 @@
 flumotion/component/converters/pipeline/Makefile
 flumotion/component/converters/overlay/Makefile
 flumotion/component/misc/Makefile
+flumotion/component/misc/cortado/Makefile
+flumotion/component/misc/cortado/cortado_location.py
 flumotion/component/misc/httpfile/Makefile
 flumotion/component/misc/porter/Makefile
 flumotion/component/misc/repeater/Makefile

Modified: flumotion/trunk/data/Makefile.am
==============================================================================
--- flumotion/trunk/data/Makefile.am	(original)
+++ flumotion/trunk/data/Makefile.am	Wed Feb  6 15:13:25 2008
@@ -16,5 +16,13 @@
 halpolicydir = $(datadir)/hal/fdi/policy/20thirdparty
 halpolicy_DATA = 91-flumotion-device-policy.fdi
 
+templatedir = $(datadir)
+template_DATA = cortado-template.html
+
 EXTRA_DIST = \
-        $(desktop_in_files) $(pixmap_DATA) $(xsl_DATA) $(script_DATA) $(halpolicy_DATA)
+        $(desktop_in_files) \
+	$(pixmap_DATA) \
+	$(xsl_DATA) \
+	$(script_DATA) \
+	$(halpolicy_DATA) \
+	$(template_DATA)

Added: flumotion/trunk/data/cortado-template.html
==============================================================================
--- (empty file)
+++ flumotion/trunk/data/cortado-template.html	Wed Feb  6 15:13:25 2008
@@ -0,0 +1,29 @@
+<html>
+  <body>
+    <object id="cortado" classid="clsid:08B0E5C0-4FCB-11CF-AAA5-00401C608501"
+            width="%(width)d" height="%(height)d" align="baseline">
+    <param name="code" value="com.fluendo.player.Cortado.class"/>
+    <param name="codebase" value="%(codebase)s"/>
+    <param name="archive" value="cortado.jar?_=2"/>
+    <param name="url" value="%(stream-url)s"/>
+    <param name="audio" value="%(has-audio)s"/>
+    <param name="video" value="%(has-video)s"/>
+    <param name="bufferSize" value="%(buffer-size)s"/>
+    <param name="framerate" value="%(framerate)d"/>
+    <param name="keepaspect" value="true"/>
+    <param name="local" value="false"/>
+    <comment>
+      <embed type="application/x-java-applet"
+             code="com.fluendo.player.Cortado.class"
+             codebase="%(codebase)s"
+             archive="cortado.jar?_=2" url="%(stream-url)s"
+             audio="%(has-audio)s" video="%(has-video)s"
+             width="%(width)d" height="%(height)d"
+             bufferSize="%(buffer-size)s" framerate="%(framerate)d"
+             keepaspect="true" local="false" align="baseline">
+        <noembed>You need to install Java to view this media file.</noembed>
+      </embed>
+    </comment>
+    </object>
+  </body>
+</html>

Modified: flumotion/trunk/data/glade/wizard_http.glade
==============================================================================
--- flumotion/trunk/data/glade/wizard_http.glade	(original)
+++ flumotion/trunk/data/glade/wizard_http.glade	Wed Feb  6 15:13:25 2008
@@ -21,7 +21,7 @@
   <child>
     <widget class="GtkTable" id="table2">
       <property name="visible">True</property>
-      <property name="n_rows">5</property>
+      <property name="n_rows">6</property>
       <property name="n_columns">3</property>
       <property name="homogeneous">False</property>
       <property name="row_spacing">6</property>
@@ -40,7 +40,7 @@
 	  <property name="yalign">0.5</property>
 	  <property name="xpad">0</property>
 	  <property name="ypad">0</property>
-	  <property name="mnemonic_widget">spinbutton_port</property>
+	  <property name="mnemonic_widget">port</property>
 	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 	  <property name="width_chars">-1</property>
 	  <property name="single_line_mode">False</property>
@@ -125,7 +125,7 @@
 	  <property name="yalign">0.5</property>
 	  <property name="xpad">0</property>
 	  <property name="ypad">0</property>
-	  <property name="mnemonic_widget">entry_mount_point</property>
+	  <property name="mnemonic_widget">mount_point</property>
 	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 	  <property name="width_chars">-1</property>
 	  <property name="single_line_mode">False</property>
@@ -154,7 +154,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkCheckButton" id="checkbutton_client_limit">
+	    <widget class="GtkCheckButton" id="has_client_limit">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="label" translatable="yes">User limit:</property>
@@ -164,7 +164,7 @@
 	      <property name="active">True</property>
 	      <property name="inconsistent">False</property>
 	      <property name="draw_indicator">True</property>
-	      <signal name="toggled" handler="on_checkbutton_client_limit_toggled" last_modification_time="Tue, 12 Jun 2007 14:56:59 GMT"/>
+	      <signal name="toggled" handler="on_has_client_limit_toggled" last_modification_time="Tue, 12 Jun 2007 14:56:59 GMT"/>
 	    </widget>
 	  </child>
 	</widget>
@@ -191,7 +191,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkCheckButton" id="checkbutton_bandwidth_limit">
+	    <widget class="GtkCheckButton" id="has_bandwidth_limit">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="label" translatable="yes">Bandwidth limit:</property>
@@ -201,7 +201,7 @@
 	      <property name="active">False</property>
 	      <property name="inconsistent">False</property>
 	      <property name="draw_indicator">True</property>
-	      <signal name="toggled" handler="on_checkbutton_bandwidth_limit_toggled" last_modification_time="Tue, 12 Jun 2007 14:57:04 GMT"/>
+	      <signal name="toggled" handler="on_has_bandwidth_limit_toggled" last_modification_time="Tue, 12 Jun 2007 14:57:04 GMT"/>
 	    </widget>
 	  </child>
 	</widget>
@@ -228,7 +228,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkCheckButton" id="checkbutton_burst_on_connect">
+	    <widget class="GtkCheckButton" id="burst_on_connect">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="label" translatable="yes">Bu_rst on connect</property>
@@ -264,7 +264,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkSpinButton" id="spinbutton_port">
+	    <widget class="GtkSpinButton" id="port">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="climb_rate">1</property>
@@ -300,7 +300,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkSpinButton" id="spinbutton_client_limit">
+	    <widget class="GtkSpinButton" id="client_limit">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="climb_rate">1</property>
@@ -336,7 +336,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkSpinButton" id="spinbutton_bandwidth_limit">
+	    <widget class="GtkSpinButton" id="bandwidth_limit">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="climb_rate">1</property>
@@ -372,7 +372,7 @@
 	  <property name="right_padding">0</property>
 
 	  <child>
-	    <widget class="GtkEntry" id="entry_mount_point">
+	    <widget class="GtkEntry" id="mount_point">
 	      <property name="width_request">100</property>
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
@@ -383,7 +383,7 @@
 	      <property name="has_frame">True</property>
 	      <property name="invisible_char">*</property>
 	      <property name="activates_default">False</property>
-	      <signal name="changed" handler="on_entry_mount_point_changed" last_modification_time="Sun, 05 Jun 2005 16:45:39 GMT"/>
+	      <signal name="changed" handler="on_mount_point_changed" last_modification_time="Sun, 05 Jun 2005 16:45:39 GMT"/>
 	    </widget>
 	  </child>
 	</widget>
@@ -396,6 +396,27 @@
 	  <property name="y_options">fill</property>
 	</packing>
       </child>
+
+      <child>
+	<widget class="GtkCheckButton" id="has_cortado">
+	  <property name="can_focus">True</property>
+	  <property name="label" translatable="yes">Embed Java applet</property>
+	  <property name="use_underline">True</property>
+	  <property name="relief">GTK_RELIEF_NORMAL</property>
+	  <property name="focus_on_click">True</property>
+	  <property name="active">False</property>
+	  <property name="inconsistent">False</property>
+	  <property name="draw_indicator">True</property>
+	</widget>
+	<packing>
+	  <property name="left_attach">0</property>
+	  <property name="right_attach">1</property>
+	  <property name="top_attach">5</property>
+	  <property name="bottom_attach">6</property>
+	  <property name="x_options">fill</property>
+	  <property name="y_options"></property>
+	</packing>
+      </child>
     </widget>
   </child>
 </widget>

Modified: flumotion/trunk/flumotion/component/consumers/httpstreamer/http_wizard.py
==============================================================================
--- flumotion/trunk/flumotion/component/consumers/httpstreamer/http_wizard.py	(original)
+++ flumotion/trunk/flumotion/component/consumers/httpstreamer/http_wizard.py	Wed Feb  6 15:13:25 2008
@@ -20,14 +20,182 @@
 # Headers in this file shall remain intact.
 
 import gettext
+import random
 
 from flumotion.configure import configure
+from flumotion.component.misc.cortado.cortado_location import CORTADO_FILENAME
 from flumotion.wizard.basesteps import WorkerWizardStep
+from flumotion.wizard.models import Component, Consumer, Plug
 
 __version__ = "$Rev$"
 _ = gettext.gettext
 X_ = _
 
+def _generateRandomString(numchars):
+    """Generate a random US-ASCII string of length numchars
+    """
+    str = ""
+    chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+    for _ in range(numchars):
+        str += chars[random.randint(0, len(chars)-1)]
+
+    return str
+
+
+class HTTPPorter(Component):
+    component_type = 'porter'
+    def __init__(self, streamer):
+        super(HTTPPorter, self).__init__(worker=streamer.worker)
+        self.properties.socket_path = streamer.socket_path
+        self.properties.port = streamer.properties.port
+        self.properties.username = streamer.porter_username
+        self.properties.password = streamer.porter_password
+
+    # Component
+
+    def getProperties(self):
+        properties = super(HTTPPorter, self).getProperties()
+        # FIXME: kiwi should do this.
+        properties['port'] = int(properties['port'])
+        return properties
+
+
+class HTTPStreamer(Consumer):
+    """
+    @ivar has_client_limit: If a client limit was set
+    @ivar has_bandwidth_limit: If a bandwidth limit was set
+    @ivar has_cortado: If we should embed cortado
+    @ivar socket_path: Path to the porter socket
+    @ivar porter_username: Username for the porter
+    @ivar porter_password: Password for the porter
+    """
+    component_type = 'http-streamer'
+    def __init__(self):
+        super(HTTPStreamer, self).__init__()
+        self.has_client_limit = True
+        self.has_bandwidth_limit = False
+        self.has_cortado = False
+        self.socket_path = '/tmp/flu-xxx.socket'
+        self.porter_username = _generateRandomString(12)
+        self.porter_password = _generateRandomString(12)
+
+    def getURL(self):
+        """Fetch the url to this stream
+        @returns: the url
+        """
+        return 'http://%s:%d%s' % (
+            self.properties.get('hostname', self.worker),
+            self.properties.port,
+            self.properties.mount_point)
+
+    # Component
+
+    def getProperties(self):
+        properties = super(HTTPStreamer, self).getProperties()
+        if self.has_bandwidth_limit:
+            properties['bandwidth-limit'] = int(
+                properties['bandwidth-limit'] * 1e6)
+        else:
+            del properties['bandwidth-limit']
+
+        if not self.has_client_limit:
+            del properties['client-limit']
+
+        if self.has_cortado:
+            properties['porter-socket-path'] = self.socket_path
+            properties['porter-username'] = self.porter_username
+            properties['porter-password'] = self.porter_password
+            properties['type'] = 'slave'
+            del properties['port']
+
+        return properties
+
+
+class CortadoPlug(Plug):
+    plug_type = "cortado-plug"
+    socket = "flumotion.component.misc.cortado.cortado.CortadoPlug"
+    def __init__(self, server, streamer, audio_producer, video_producer):
+        """
+        @param server: server
+        @type  server: L{CortadoHTTPServer}
+        @param streamer: streamer
+        @type  streamer: L{HTTPStreamer}
+        @param audio_producer: audio producer
+        @type  audio_producer: L{flumotion.wizard.models.AudioProducer} subclass or None
+        @param video_producer: video producer
+        @type  video_producer: L{flumotion.wizard.models.VideoProducer} subclass or None
+        """
+        super(CortadoPlug, self).__init__()
+        self.server = server
+        self.streamer = streamer
+        self.audio_producer = audio_producer
+        self.video_producer = video_producer
+
+    # Component
+
+    def getProperties(self):
+        p = super(CortadoPlug, self).getProperties()
+
+        p['codebase'] = self.server.getCodebase()
+        p['stream-url'] = self.streamer.getURL()
+        p['has-video'] = self.video_producer is not None
+        p['has-audio'] = self.audio_producer is not None
+
+        if self.video_producer:
+            width = self.video_producer.properties.width
+            height = self.video_producer.properties.height
+            framerate = self.video_producer.properties.framerate
+        else:
+            width = 320
+            height = 240
+            framerate = 1
+
+        p['width'] = width
+        p['height'] = height
+        p['framerate'] = framerate
+        p['buffer-size'] = 40
+
+        return p
+
+
+class CortadoHTTPServer(Component):
+    """
+    This is a component which serves cortado on /cortado/index.html
+    It shares the port of a http-streamer through a porter.
+    """
+    component_type = 'http-server'
+    def __init__(self, streamer, audio_producer, video_producer):
+        """
+        @param streamer: streamer
+        @type  streamer: L{HTTPStreamer}
+        @param audio_producer: audio producer
+        @type  audio_producer: L{flumotion.wizard.models.AudioProducer}
+           subclass or None
+        @param video_producer: video producer
+        @type  video_producer: L{flumotion.wizard.models.VideoProducer}
+           subclass or None
+        """
+        self.streamer = streamer
+
+        super(CortadoHTTPServer, self).__init__(worker=streamer.worker)
+
+        self.properties.mount_point = "/cortado"
+        self.properties.porter_socket_path = streamer.socket_path
+        self.properties.porter_username = streamer.porter_username
+        self.properties.porter_password = streamer.porter_password
+        self.properties.type = 'slave'
+
+        plug = CortadoPlug(self, streamer, audio_producer, video_producer)
+        self.addPlug(plug)
+
+    def getCodebase(self):
+        """Returns the base of directory of the applet
+        @returns: directory
+        """
+        return 'http://%s:%d%s/' % (self.worker,
+                                    self.streamer.properties.port,
+                                    self.properties.mount_point)
+
 
 class HTTPStep(WorkerWizardStep):
     glade_file = 'wizard_http.glade'
@@ -36,40 +204,105 @@
 
     def __init__(self, wizard):
         self._blocked = False
+        self.model = HTTPStreamer()
         WorkerWizardStep.__init__(self, wizard)
 
+    def getStreamerConsumer(self):
+        """Returns the http-streamer consumer model
+        @returns: the streamer consumer
+        """
+        return self.model
+
+    def getServerConsumer(self):
+        """Returns the http-server consumer model or None
+        if there will only a stream served.
+        @returns: the server consumer or None
+        """
+        if not self.model.has_cortado:
+            return None
+
+        source_step = self.wizard.get_step('Source')
+
+        return CortadoHTTPServer(self.model,
+                                 source_step.get_audio_producer(),
+                                 source_step.get_video_producer())
+
+    def getPorter(self):
+        """Returns the porter model or None if there will only a stream served.
+        @returns: the porter or None
+        """
+        if not self.model.has_cortado:
+            return None
+
+        return HTTPPorter(self.model)
+
     # WizardStep
 
     def setup(self):
-        self.spinbutton_port.set_value(self.port)
+        self.port.data_type = int
+        self.mount_point.data_type = str
+        self.client_limit.data_type = int
+        self.bandwidth_limit.data_type = float
+
+        self.port.set_value(self.default_port)
+
+        if self._canEmbedCortado():
+            self.model.has_cortado = True
+            self.has_cortado.show()
+
+        self.add_proxy(self.model,
+                       ['has_client_limit',
+                        'has_bandwidth_limit',
+                        'has_cortado'])
+
+        self.add_proxy(self.model.properties,
+                       ['port',
+                        'client_limit',
+                        'bandwidth_limit',
+                        'mount_point',
+                        'burst_on_connect'])
+
+        self.mount_point.set_text("/")
 
     def activated(self):
         self._check_elements()
         self._verify()
 
     def worker_changed(self):
+        self.model.worker = self.worker
         self._check_elements()
 
-    def get_state(self):
-        options = {
-            'mount-point': self.entry_mount_point.get_text(),
-            'burst-on-connect': self.checkbutton_burst_on_connect.get_active(),
-            'port': int(self.spinbutton_port.get_value()),
-            }
-
-        if not self.checkbutton_bandwidth_limit.get_active():
-            options['bandwidth-limit'] = int(
-                self.spinbutton_bandwidth_limit.get_value() * 1e6)
-        if not self.checkbutton_client_limit.get_active():
-            options['client-limit'] = int(
-                self.spinbutton_client_limit.get_value())
-        return options
-
     def get_next(self):
         return self.wizard.get_step('Consumption').get_next(self)
 
     # Private
 
+    def _canEmbedCortado(self):
+        # Empty string means that it couldn't be found by configure
+        if not CORTADO_FILENAME:
+            return False
+
+        encoding_step = self.wizard.get_step('Encoding')
+        if encoding_step.get_muxer_type() not in [
+            'ogg-muxer',
+            'multipart-muxer']:
+            return False
+
+        audio_encoder = encoding_step.get_audio_encoder()
+        if audio_encoder and audio_encoder.component_type not in [
+            'vorbis-encoder',
+            'mulaw-encoder']:
+            return False
+
+        video_encoder = encoding_step.get_video_encoder()
+        if video_encoder and video_encoder.component_type not in [
+            'theora-encoder',
+            'jpeg-encoder',
+            'smoke-encoder']:
+            return False
+
+        return True
+
     def _check_elements(self):
         def got_missing(missing):
             blocked = bool(missing)
@@ -81,10 +314,10 @@
         d.addCallback(got_missing)
 
     def _verify(self):
-        self.spinbutton_client_limit.set_sensitive(
-            self.checkbutton_client_limit.get_active())
-        self.spinbutton_bandwidth_limit.set_sensitive(
-            self.checkbutton_bandwidth_limit.get_active())
+        self.client_limit.set_sensitive(
+            self.has_client_limit.get_active())
+        self.bandwidth_limit.set_sensitive(
+            self.has_bandwidth_limit.get_active())
         self._update_blocked()
 
     def _block_next(self, blocked):
@@ -95,35 +328,36 @@
 
     def _update_blocked(self):
         self.wizard.block_next(
-            self._blocked or self.entry_mount_point.get_text() == '')
+            self._blocked or self.mount_point.get_text() == '')
 
     # Callbacks
 
-    def on_entry_mount_point_changed(self, entry):
+    def on_mount_point_changed(self, entry):
         self._verify()
+        self._block_next(self.model.has_cortado and entry.get_text() == "/")
 
-    def on_checkbutton_client_limit_toggled(self, checkbutton):
+    def on_has_client_limit_toggled(self, checkbutton):
         self._verify()
 
-    def on_checkbutton_bandwidth_limit_toggled(self, checkbutton):
+    def on_has_bandwidth_limit_toggled(self, checkbutton):
         self._verify()
 
 
 class HTTPBothStep(HTTPStep):
     name = _('HTTP Streamer (audio & video)')
     sidebar_name = _('HTTP audio/video')
-    port = configure.defaultStreamPortRange[0]
+    default_port = configure.defaultStreamPortRange[0]
 
 
 class HTTPAudioStep(HTTPStep):
     name = _('HTTP Streamer (audio only)')
     sidebar_name = _('HTTP audio')
-    port = configure.defaultStreamPortRange[1]
+    default_port = configure.defaultStreamPortRange[1]
 
 
 class HTTPVideoStep(HTTPStep):
     name = _('HTTP Streamer (video only)')
     sidebar_name = _('HTTP video')
-    port = configure.defaultStreamPortRange[2]
+    default_port = configure.defaultStreamPortRange[2]
 
 

Modified: flumotion/trunk/flumotion/component/misc/Makefile.am
==============================================================================
--- flumotion/trunk/flumotion/component/misc/Makefile.am	(original)
+++ flumotion/trunk/flumotion/component/misc/Makefile.am	Wed Feb  6 15:13:25 2008
@@ -1,5 +1,5 @@
 include $(top_srcdir)/common/python.mk
-                                                                                
+
 miscdir = $(libdir)/flumotion/python/flumotion/component/misc
 misc_PYTHON = \
 	__init__.py
@@ -10,8 +10,9 @@
 	rm -rf *.pyc *.pyo
 
 SUBDIRS = \
+	cortado \
+	httpfile \
 	porter \
-	repeater \
-	httpfile
+	repeater
 
 EXTRA_DIST = $(misc_DATA)

Added: flumotion/trunk/flumotion/component/misc/cortado/Makefile.am
==============================================================================
--- (empty file)
+++ flumotion/trunk/flumotion/component/misc/cortado/Makefile.am	Wed Feb  6 15:13:25 2008
@@ -0,0 +1,18 @@
+include $(top_srcdir)/common/python.mk
+
+cortadodir = $(libdir)/flumotion/python/flumotion/component/misc/cortado
+
+cortado_PYTHON = \
+	__init__.py 	\
+	cortado.py
+
+cortado_DATA = cortado.xml
+
+TAGS_FILES = $(cortado_PYTHON)
+
+clean-local:
+	rm -rf *.pyc *.pyo
+
+EXTRA_DIST = $(cortado_PYTHON) $(cortado_DATA) cortado_location.py.in
+CLEAN_FILES = cortado_location.py
+BUILT_SOURCES = cortado_location.py

Added: flumotion/trunk/flumotion/component/misc/cortado/__init__.py
==============================================================================
--- (empty file)
+++ flumotion/trunk/flumotion/component/misc/cortado/__init__.py	Wed Feb  6 15:13:25 2008
@@ -0,0 +1,26 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+#
+# Flumotion - a streaming media server
+# Copyright (C) 2008 Fluendo, S.L. (www.fluendo.com).
+# All rights reserved.
+
+# This file may be distributed and/or modified under the terms of
+# the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+# This file is distributed without any warranty; without even the implied
+# warranty of merchantability or fitness for a particular purpose.
+# See "LICENSE.GPL" in the source distribution for more information.
+
+# Licensees having purchased or holding a valid Flumotion Advanced
+# Streaming Server license may use this file in accordance with the
+# Flumotion Advanced Streaming Server Commercial License Agreement.
+# See "LICENSE.Flumotion" in the source distribution for more information.
+
+# Headers in this file shall remain intact.
+
+"""cortado applet plug
+"""
+
+__version__ = "$Rev$"
+

Added: flumotion/trunk/flumotion/component/misc/cortado/cortado.py
==============================================================================
--- (empty file)
+++ flumotion/trunk/flumotion/component/misc/cortado/cortado.py	Wed Feb  6 15:13:25 2008
@@ -0,0 +1,144 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+#
+# Flumotion - a streaming media server
+# Copyright (C) 2008 Fluendo, S.L. (www.fluendo.com).
+# All rights reserved.
+
+# This file may be distributed and/or modified under the terms of
+# the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+# This file is distributed without any warranty; without even the implied
+# warranty of merchantability or fitness for a particular purpose.
+# See "LICENSE.GPL" in the source distribution for more information.
+
+# Licensees having purchased or holding a valid Flumotion Advanced
+# Streaming Server license may use this file in accordance with the
+# Flumotion Advanced Streaming Server Commercial License Agreement.
+# See "LICENSE.Flumotion" in the source distribution for more information.
+
+# Headers in this file shall remain intact.
+
+import os
+
+from twisted.web.resource import Resource
+from twisted.web.static import Data, File
+
+from flumotion.common import log
+from flumotion.component.misc.cortado.cortado_location import CORTADO_FILENAME
+from flumotion.component.plugs.base import ComponentPlug
+from flumotion.configure import configure
+
+__version__ = "$Rev$"
+
+def _htmlbool(value):
+    if value:
+        return 'true'
+    return 'false'
+
+
+class CortadoDirectoryResource(Resource):
+    """
+    I generate the directory used to serve a cortado applet
+    It contains::
+    - index.html - html file
+    - cortado.jar - cortado java applet
+    """
+
+    def __init__(self, mount_point, properties):
+        Resource.__init__(self)
+
+        index_name = properties.get('index', 'index.html')
+
+        root = mount_point
+        if root[-1] != "/":
+            root += "/"
+        if index_name != 'index.html':
+            root = None
+        self._mount_point_root = root
+        self._properties = properties
+        self._index_content = self._get_index_content()
+        self._index_name = index_name
+
+        self._addChildren()
+
+    def _addChildren(self):
+        self.putChild("cortado.jar",
+                      File(CORTADO_FILENAME, 'application/x-java-archive'))
+
+        self.putChild(self._index_name,
+                      self._index_content)
+
+    def _get_template_filename(self):
+        filename = self._properties.get('html-template')
+        if not filename:
+            filename = os.path.join(configure.datadir,
+                                    'cortado-template.html')
+        return filename
+
+    def _get_index_content(self):
+        html_template = self._get_template_filename()
+        ns = {}
+        ns['has-audio'] = _htmlbool(self._properties['has-audio'])
+        ns['has-video'] = _htmlbool(self._properties['has-video'])
+        for attribute in ['codebase',
+                          'width',
+                          'height',
+                          'stream-url',
+                          'buffer-size',
+                          'framerate']:
+            ns[attribute] = self._properties[attribute]
+
+        data = open(html_template, 'r').read()
+        content = data % ns
+        return Data(content, 'text/html')
+
+    # Resource
+
+    def getChildWithDefault(self, pathEl, request):
+        # Maps /index.html to /
+        if request.uri == self._mount_point_root:
+            return self._index_content
+        return Resource.getChildWithDefault(self, pathEl, request)
+
+    def render(self, request):
+        return self._index_content
+
+
+class CortadoPlug(ComponentPlug):
+    def __init__(self, args):
+        ComponentPlug.__init__(self, args)
+        self._properties = args['properties']
+
+    # ComponentPlug
+
+    def start(self, component):
+        log.debug('cortado', 'Attaching to %r' % (component,))
+        resource = CortadoDirectoryResource(component.getMountPoint(),
+                                            self._properties)
+        component.setRootResource(resource)
+
+
+def test():
+    import sys
+    from twisted.internet import reactor
+    from twisted.python.log import startLogging
+    from twisted.web.server import Site
+    startLogging(sys.stderr)
+
+    properties = {'has-audio': True,
+                  'has-video': True,
+                  'codebase': '/',
+                  'width' : 320,
+                  'height' : 240,
+                  'stream-url' : '/stream.ogg',
+                  'buffer-size': 40,
+                  'framerate' : 1}
+    root = CortadoDirectoryResource('/', properties)
+    site = Site(root)
+
+    reactor.listenTCP(8080, site)
+    reactor.run()
+
+if __name__ == "__main__":
+    test()

Added: flumotion/trunk/flumotion/component/misc/cortado/cortado.xml
==============================================================================
--- (empty file)
+++ flumotion/trunk/flumotion/component/misc/cortado/cortado.xml	Wed Feb  6 15:13:25 2008
@@ -0,0 +1,44 @@
+<registry>
+  <plugs>
+    <plug socket="flumotion.component.plugs.lifecycle.ComponentLifecycle"
+          type="cortado-plug">
+      <entry location="flumotion/component/misc/cortado/cortado.py"
+             function="CortadoPlug" />
+      <properties>
+        <property name="width" type="int" required="true"  
+                  description="width of the cortado applet" />
+        <property name="height" type="int" required="true" 
+                  description="height of the cortado applet" />
+        <property name="html-template" type="string" 
+                  description="HTML template to use to serve the cortado applet" />
+        <property name="index" type="string" 
+                  description="The name of the index file, default is index.html" />
+        <property name="has-audio" type="bool" required="true"  
+                  description="If audio should be streamed" />
+        <property name="has-video" type="bool" required="true"  
+                  description="If video should be streamed" />
+        <property name="stream-url" type="string" required="true"  
+                  description="url to the stream we're displaying" />
+        <property name="codebase" type="string" required="true"  
+                  description="baserl used to fetch the cortado applet code" />
+        <property name="buffer-size" type="int" required="true"  
+                  description="size of the buffer in cortado applet" />
+        <property name="framerate" type="float" required="true"  
+                  description="framerate used in the cortado applet" />
+      </properties>
+    </plug>
+  </plugs>
+  <bundles>
+    <bundle name="cortado-plug">
+      <dependencies>
+        <dependency name="base-plugs" />
+      </dependencies>
+      <directories>
+        <directory name="flumotion/component/misc/cortado">
+          <filename location="__init__.py" />
+          <filename location="cortado.py" />
+        </directory>
+      </directories>
+    </bundle>
+  </bundles>
+</registry>

Added: flumotion/trunk/flumotion/component/misc/cortado/cortado_location.py.in
==============================================================================
--- (empty file)
+++ flumotion/trunk/flumotion/component/misc/cortado/cortado_location.py.in	Wed Feb  6 15:13:25 2008
@@ -0,0 +1,22 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+#
+# Flumotion - a streaming media server
+# Copyright (C) 2008 Fluendo, S.L. (www.fluendo.com).
+# All rights reserved.
+
+# This file may be distributed and/or modified under the terms of
+# the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+# This file is distributed without any warranty; without even the implied
+# warranty of merchantability or fitness for a particular purpose.
+# See "LICENSE.GPL" in the source distribution for more information.
+
+# Licensees having purchased or holding a valid Flumotion Advanced
+# Streaming Server license may use this file in accordance with the
+# Flumotion Advanced Streaming Server Commercial License Agreement.
+# See "LICENSE.Flumotion" in the source distribution for more information.
+
+# Headers in this file shall remain intact.
+
+CORTADO_FILENAME = "@CORTADO_FILENAME@"

Modified: flumotion/trunk/flumotion/wizard/models.py
==============================================================================
--- flumotion/trunk/flumotion/wizard/models.py	(original)
+++ flumotion/trunk/flumotion/wizard/models.py	Wed Feb  6 15:13:25 2008
@@ -156,11 +156,12 @@
     component_type = None
     name_template = "component"
 
-    def __init__(self):
-        self.worker = None
+    def __init__(self, worker=None):
+        self.worker = worker
         self.feeders = []
         self.eaters = []
         self.properties = Properties()
+        self.plugs = []
 
     def validate(self):
         if not self.worker:
@@ -176,6 +177,34 @@
             props[key.replace('_', '-')] = value
         return props
 
+    def getPlugs(self):
+        return self.plugs
+
+    def addPlug(self, plug):
+        """
+        Add a plug to the component
+        @param plug: the plug
+        @type plug: L{Plug}
+        """
+        self.plugs.append(plug)
+
+
+class Plug(object):
+    """I am a Plug.
+    A plug has a name which identifies it and must be unique
+    within a flow.
+    @cvar plug_type: the type of the plug, such as cortado,
+      this is not mandatory in the class, can also be set in the instance.
+    """
+    def __init__(self):
+        self.properties = Properties()
+
+    def getProperties(self):
+        props = {}
+        for key, value in self.properties.iteritems():
+            props[key.replace('_', '-')] = value
+        return props
+
 
 class Producer(Component):
     """I am a component which produces data.

Modified: flumotion/trunk/flumotion/wizard/save.py
==============================================================================
--- flumotion/trunk/flumotion/wizard/save.py	(original)
+++ flumotion/trunk/flumotion/wizard/save.py	Wed Feb  6 15:13:25 2008
@@ -40,12 +40,17 @@
 class Component(log.Loggable):
     logCategory = "componentsave"
 
-    def __init__(self, name, component_type, worker, properties={}):
+    def __init__(self, name, component_type, worker, properties=None, plugs=None):
         self.debug('Creating component %s (%s) worker=%r' % (
             name, type, worker))
         self.name = name
         self.component_type = component_type
+        if not properties:
+            properties = {}
         self.props = properties
+        if not plugs:
+            plugs = []
+        self.plugs = plugs
         self.worker = worker
         self.eaters = []
         self.feeders = []
@@ -105,6 +110,21 @@
                 value = self.props[name]
                 s += '      <property name="%s">%s</property>\n' % (name, value)
 
+        if self.plugs:
+            s += "\n"
+            s += '      <plugs>\n'
+            for plug in self.plugs:
+                s += '      <plug socket="%s" type="%s">\n' % (plug.socket,
+                                                               plug.plug_type)
+                plugprops = plug.getProperties()
+                property_names = plugprops.keys()
+                property_names.sort()
+                for name in property_names:
+                    value = plugprops[name]
+                    s += '        <property name="%s">%s</property>\n' % (
+                        name, value)
+                s += "      </plug>\n"
+            s += '      </plugs>\n'
         s += "    </component>\n"
         return s
 
@@ -118,6 +138,8 @@
     logCategory = 'wizard-saver'
     def __init__(self, wizard):
         self.wizard = wizard
+        self._flow_components = []
+        self._atmosphere_components = []
 
     def _set_fraction_property(self, properties, property_name, denominator):
         if not property_name in properties:
@@ -185,6 +207,30 @@
                          encoding_step.get_muxer_type(),
                          encoding_step.worker)
 
+    def _handleHTTPConsumer(self, name, step):
+        server = step.getServerConsumer()
+        if server is not None:
+            server = Component('server1',
+                               server.component_type,
+                               server.getWorker(),
+                               server.getProperties(),
+                               server.getPlugs())
+            self._flow_components.append(server)
+
+        porter = step.getPorter()
+        if porter is not None:
+            porter = Component('porter1',
+                               porter.component_type,
+                               porter.getWorker(),
+                               porter.getProperties())
+            self._atmosphere_components.append(porter)
+
+        streamer = step.getStreamerConsumer()
+        return Component(name,
+                         streamer.component_type,
+                         streamer.getWorker(),
+                         streamer.getProperties())
+
     def getVideoOverlay(self, video_source):
         step = self.wizard.get_step('Overlay')
         properties = step.get_state()
@@ -218,35 +264,35 @@
         return Component('overlay-video', 'overlay-converter',
                          step.worker, properties)
 
-    def handleVideo(self, components):
+    def handleVideo(self):
         video_source = self._getVideoSource()
-        components.append(video_source)
+        self._flow_components.append(video_source)
 
         video_encoder = self._getVideoEncoder()
-        components.append(video_encoder)
+        self._flow_components.append(video_encoder)
 
         video_overlay = self.getVideoOverlay(video_source)
         if video_overlay:
             video_overlay.link(video_source)
             video_encoder.link(video_overlay)
-            components.append(video_overlay)
+            self._flow_components.append(video_overlay)
         else:
             video_encoder.link(video_source)
         return video_encoder, video_source
 
-    def handleAudio(self, components, video_source):
+    def handleAudio(self, video_source):
         audio_source = self._getAudioSource(video_source)
         # In case of firewire component, which can already be there
-        if not audio_source in components:
-            components.append(audio_source)
+        if not audio_source in self._flow_components:
+            self._flow_components.append(audio_source)
 
         audio_encoder = self._getAudioEncoder()
-        components.append(audio_encoder)
+        self._flow_components.append(audio_encoder)
         audio_encoder.link(audio_source)
 
         return audio_encoder
 
-    def handleConsumers(self, components, audio_encoder, video_encoder):
+    def handleConsumers(self, audio_encoder, video_encoder):
         cons_options = self.wizard.get_step_options('Consumption')
         has_audio = self.wizard.get_step_option('Source', 'has-audio')
         has_video = self.wizard.get_step_option('Source', 'has-video')
@@ -323,55 +369,65 @@
             if not cons_options.has_key(name):
                 continue
             step = self.wizard.get_step(step_name)
-            consumer = Component(name, comp_type, step.worker,
-                                 step.get_state())
+            if comp_type == 'http-streamer':
+                consumer = self._handleHTTPConsumer(name, step)
+            else:
+                consumer = Component(
+                    name, comp_type,
+                    step.worker, step.get_state())
+
             consumer.link(muxer)
-            components.append(consumer)
+            self._flow_components.append(consumer)
 
         # Add & link the muxers we will use
         if audio_muxer and audio_muxer.eaters:
-            components.append(audio_muxer)
+            self._flow_components.append(audio_muxer)
             audio_muxer.link(audio_encoder)
         if video_muxer and video_muxer.eaters:
-            components.append(video_muxer)
+            self._flow_components.append(video_muxer)
             video_muxer.link(video_encoder)
         if both_muxer and both_muxer.eaters:
-            components.append(both_muxer)
+            self._flow_components.append(both_muxer)
             both_muxer.link(video_encoder)
             both_muxer.link(audio_encoder)
 
-    def getComponents(self):
+    def _fetchComponentsFromWizardSteps(self):
         source_options = self.wizard.get_step_options('Source')
         has_video = source_options['has-video']
         has_audio = source_options['has-audio']
 
-        components = []
-
         video_encoder = None
         video_source = None
         if has_video:
-            video_encoder, video_source = self.handleVideo(components)
+            video_encoder, video_source = self.handleVideo()
 
         # Must do audio after video, in case of a firewire audio component
         # is selected together with a firewire video component
         audio_encoder = None
         if has_audio:
-            audio_encoder = self.handleAudio(components, video_source)
-
-        self.handleConsumers(components, audio_encoder, video_encoder)
+            audio_encoder = self.handleAudio(video_source)
 
-        return components
+        self.handleConsumers(audio_encoder, video_encoder)
 
     def getXML(self):
         # FIXME: allow for naming flows !
-        components = self.getComponents()
-        self.debug('Got %d components' % len(components))
+        self._fetchComponentsFromWizardSteps()
+        flowname = self.wizard.flowName
 
         s = '<planet>\n'
-        s += '  <flow name="%s">\n' % self.wizard.flowName
-        for component in components:
-            s += component.toXML() + "\n"
-        s += '  </flow>\n'
+
+        if self._atmosphere_components:
+            s += '  <atmosphere>\n'
+            for component in self._atmosphere_components:
+                s += component.toXML() + "\n"
+            s += '  </atmosphere>\n'
+
+        if self._flow_components:
+            s += '  <flow name="%s">\n' % flowname
+            for component in self._flow_components:
+                s += component.toXML() + "\n"
+            s += '  </flow>\n'
+
         s += '</planet>\n'
 
         return s


More information about the flumotion-commit mailing list