1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """HTTP wizard integration
23
24 This provides a step which you can chose:
25 - http port
26 - bandwidth/client limit
27 - mount point (eg, the url it will be accessed as)
28 - burst on connect
29 - cortado java applet
30
31 A component of type 'http-streamer' will always be created.
32 In addition, if you include the java applet, a 'porter' and
33 'http-server' will be included to share the port between the streamer
34 and the server and to serve an html file plus the java applet itself.
35 On the http-server the applet will be provided with help of a plug.
36 """
37
38 import gettext
39
40 import gobject
41 from kiwi.utils import gsignal
42 import gtk
43 from twisted.internet import defer
44
45 from flumotion.common import errors, log, messages
46 from flumotion.common.i18n import N_, gettexter, ngettext
47 from flumotion.wizard.models import Consumer
48 from flumotion.wizard.basesteps import ConsumerStep
49
50 __version__ = "$Rev: 7015 $"
51 _ = gettext.gettext
52 T_ = gettexter()
53
54
56 """I am a model representing the configuration file for a
57 HTTP streamer component.
58 @ivar has_client_limit: If a client limit was set
59 @ivar has_bandwidth_limit: If a bandwidth limit was set
60 @ivar has_cortado: If we should embed cortado
61 @ivar hostname: the hostname this will be streamed on
62 """
63 componentType = 'http-streamer'
65 super(HTTPStreamer, self).__init__()
66 self._common = common
67 self.has_cortado = False
68 self.has_plugins = False
69 self.hostname = None
70
71
72
74 """Fetch the url to this stream
75 @returns: the url
76 """
77 return 'http://%s:%d%s' % (
78 self.properties.get('hostname', self.hostname),
79 self.getPorter().getPort(),
80 self.properties.mount_point)
81
82
83
98
99
101 return self._common.port
102
103
105 """I am a line in the plug plugin area representing a single plugin.
106 Rendered, I am visible as a checkbutton containing a label with the
107 description of the plugin.
108 Signals::
109 - enable-changed: emitted when I am enabled/disabled
110 @ivar plugin: plugin instance
111 """
112 gsignal('enable-changed')
113 - def __init__(self, plugin, description):
114 """
115 @param plugin: plugin instance
116 @param description: description of the plugin
117 """
118 gtk.VBox.__init__(self)
119 self.plugin = plugin
120 self.checkbutton = gtk.CheckButton(description)
121 self.checkbutton.connect('toggled',
122 self._on_checkbutton__toggled)
123 self.checkbutton.set_active(True)
124 self.pack_start(self.checkbutton)
125 self.checkbutton.show()
126
128 """Find out if the plugin is going to be enabled or not
129 @returns: enabled
130 @rtype: bool
131 """
132 return self.checkbutton.get_active()
133
136 gobject.type_register(PlugPluginLine)
137
138
140 """I am plugin area representing all available plugins. I keep track
141 of the plugins and their internal state. You can ask me to add new plugins
142 or get the internal models of the plugins.
143 """
145 self.streamer = streamer
146 gtk.VBox.__init__(self, spacing=6)
147 self._lines = []
148
149
150
151 - def addPlug(self, plugin, description):
163
165 """Fetch a list of server consumers which are going to be used by all
166 available plugins.
167 @returns: consumers
168 @rtype: a sequence of L{HTTPServer} subclasses
169 """
170 for plugin in self._getEnabledPlugins():
171 yield plugin.getConsumer(self.streamer, audio_producer,
172 video_producer)
173
174
175
177 for line in self._lines:
178 if line.isEnabled():
179 return True
180 return False
181
183 for line in self._lines:
184 if line.isEnabled():
185 yield line.plugin
186
189
190
191
194
195
197 """I am a step of the configuration wizard which allows you
198 to configure a stream to be served over HTTP.
199 """
200 gladeFile = 'httpstreamer-wizard.glade'
201
207
208
209
212
214 return 'http-streamer'
215
220
225
226
227
239
243
247
248
249
251 def gotEntries(entries):
252 log.debug('httpwizard', 'got %r' % (entries,))
253 for entry in entries:
254 if not self._canAddPlug(entry):
255 continue
256 def response(factory, entry):
257
258 plugin = factory(self.wizard)
259 if hasattr(plugin, 'workerChanged'):
260 d = plugin.workerChanged(self.worker)
261 def cb(found, plugin, entry):
262 if found:
263 self._addPlug(
264 plugin, N_(entry.description))
265 d.addCallback(cb, plugin, entry)
266 else:
267 self._addPlug(plugin, N_(entry.description))
268 d = self.wizard.getWizardPlugEntry(entry.componentType)
269 d.addCallback(response, entry)
270
271 d = self.wizard.getWizardEntries(wizardTypes=['http-consumer'])
272 d.addCallbacks(gotEntries)
273
275
276
277 muxerTypes = []
278 audioTypes = []
279 videoTypes = []
280 for mediaType in entry.getAcceptedMediaTypes():
281 kind, name = mediaType.split(':', 1)
282 if kind == 'muxer':
283 muxerTypes.append(name)
284 elif kind == 'video':
285 videoTypes.append(name)
286 elif kind == 'audio':
287 audioTypes.append(name)
288 else:
289 raise AssertionError
290
291 encoding_step = self.wizard.getStep('Encoding')
292 if encoding_step.getMuxerFormat() not in muxerTypes:
293 return False
294
295 audioFormat = encoding_step.getAudioFormat()
296 videoFormat = encoding_step.getVideoFormat()
297 if ((audioFormat and audioFormat not in audioTypes) or
298 (videoFormat and videoFormat not in videoTypes)):
299 return False
300
301 return True
302
303 - def _addPlug(self, plugin, description):
305
307 self.wizard.waitForTask('http streamer check')
308
309 def importError(failure):
310 print 'FIXME: trap', failure, 'in .../httpstreamer/wizard_gtk.py'
311 self.info('could not import twisted-web')
312 message = messages.Warning(T_(N_(
313 "Worker '%s' cannot import module '%s'."),
314 self.worker, 'twisted.web'))
315 message.add(T_(N_("\nThis module is part of the '%s'."),
316 'Twisted Project'))
317 message.add(T_(N_("\nThe project's homepage is %s"),
318 'http://www.twistedmatrix.com/'))
319 message.id = 'module-twisted-web'
320 self.wizard.add_msg(message)
321 self.wizard.taskFinished(True)
322
323 def finished(hostname):
324 self.model.hostname = hostname
325 self.wizard.taskFinished()
326
327 def checkWorkerHostname(unused):
328 d = self.wizard.runInWorker(
329 self.worker, 'flumotion.worker.checks.http',
330 'runHTTPStreamerChecks')
331 d.addCallback(finished)
332
333 def checkElements(elements):
334 if elements:
335 f = ngettext("Worker '%s' is missing GStreamer element '%s'.",
336 "Worker '%s' is missing GStreamer elements '%s'.",
337 len(elements))
338 message = messages.Warning(
339 T_(f, self.worker, "', '".join(elements)), id='httpstreamer')
340 self.wizard.add_msg(message)
341 self.wizard.taskFinished(True)
342 return defer.fail(errors.FlumotionError('missing multifdsink element'))
343
344 self.wizard.clear_msg('httpstreamer')
345
346
347 d = self.wizard.checkImport(self.worker, 'twisted.web')
348 d.addCallback(checkWorkerHostname)
349 d.addErrback(importError)
350
351
352 d = self.wizard.requireElements(self.worker, 'multifdsink')
353 d.addCallback(checkElements)
354
355
356
357 d.addErrback(lambda unused: self.wizard.taskFinished(True))
358
361
367
368
369
374
375
377 name = 'HTTPStreamerBoth'
378 title = _('HTTP Streamer (audio and video)')
379 sidebarName = _('HTTP audio/video')
380
381
382
385
386
388 name = 'HTTPStreamerAudio'
389 title = _('HTTP Streamer (audio only)')
390 sidebarName = _('HTTP audio')
391
392
393
396
397
399 name = 'HTTPStreamerVideo'
400 title = _('HTTP Streamer (video only)')
401 sidebarName = _('HTTP video')
402
403
404
407