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