summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/examples/elevate2018.json138
-rwxr-xr-xsrc/flufigut.py96
2 files changed, 214 insertions, 20 deletions
diff --git a/src/examples/elevate2018.json b/src/examples/elevate2018.json
new file mode 100644
index 0000000..c006310
--- /dev/null
+++ b/src/examples/elevate2018.json
@@ -0,0 +1,138 @@
+{
+ "globals":{
+ "templates": "default",
+ "version": "0.10.1",
+ "name": "elevate-live",
+ "description": "Live from Elevate Festival 2018",
+ "manager": {
+ "machine": "telesto",
+ "host": "109.73.158.69",
+ "port": 7531,
+ "transport": "ssl"
+ },
+ "admin": {
+ "username": "equinox",
+ "password": "change-me"
+ },
+ "stats": {
+ "rrd": {
+ "directory": "/var/lib/flumotion/rrd"
+ }
+ },
+ "resolutions": {
+ "720p25": { "width": 1280, "height": 720, "rate": "25/1" },
+ "480p25": { "width": 854, "height": 480, "rate": "25/1" },
+ "360p25": { "width": 640, "height": 360, "rate": "25/1" },
+ "240p25": { "width": 426, "height": 240, "rate": "25/1" }
+ },
+ "formats": {
+ "flash": { "muxer": "flv", "video": "h264", "audio": "aac", "samplerate": 44100 },
+ "webm": { "muxer": "webm", "video": "vp8", "audio": "vorbis" },
+ "ogg": { "muxer": "ogg", "video": "theora", "audio": "vorbis" },
+ "mp3": { "muxer": "mp3", "video": "null", "audio": "mp3" },
+ "rec": { "muxer": "mkv", "video": "mjpeg", "audio": "raw" }
+ },
+ "profiles": {
+ "high": { "video": "720p25", "audio": 160 },
+ "medium": { "video": "480p25", "audio": 128 },
+ "low": { "video": "360p25", "audio": 96 },
+ "mini": { "video": "240p25", "audio": 64 },
+ "full": { "video": "720p25", "audio": 0 }
+ },
+ "bitrates": {
+ "h264": { "720p25": 1800, "480p25": 1000, "360p25": 600, "240p25": 300 },
+ "vp8": { "720p25": 1800, "480p25": 1000, "360p25": 600, "240p25": 300 },
+ "mjpeg": { "720p25": 95 }
+ },
+ "workers": {
+ "telesto": [ "input-sdi-orig", "resize-sdi-orig", "resample-sdi-orig",
+ "encoder-sdi-orig-vorbis-160", "encoder-sdi-orig-vorbis-128", "encoder-sdi-orig-vorbis-96", "encoder-sdi-orig-vorbis-64",
+ "encoder-sdi-orig-aac-160", "encoder-sdi-orig-aac-128", "encoder-sdi-orig-aac-96", "encoder-sdi-orig-aac-64",
+ "encoder-sdi-orig-mp3-160", "encoder-sdi-orig-mp3-128", "encoder-sdi-orig-mp3-96", "encoder-sdi-orig-mp3-64",
+ "encoder-sdi-orig-h264-720p25", "encoder-sdi-orig-h264-480p25", "encoder-sdi-orig-h264-240p25",
+ "encoder-sdi-orig-mjpeg-720p25", "muxer-avr-rec-full", "encoder-sdi-orig-raw-0" ],
+ "calypso": [ "encoder-sdi-orig-vp8-720p25", "encoder-sdi-orig-vp8-480p25", "encoder-sdi-orig-vp8-360p25", "encoder-sdi-orig-vp8-240p25",
+ "encoder-sdi-orig-h264-360p25",
+ "muxer-av-orig-webm-high", "muxer-av-orig-webm-medium", "muxer-av-orig-webm-low", "muxer-av-orig-webm-mini",
+ "muxer-av-orig-flash-high", "muxer-av-orig-flash-medium", "muxer-av-orig-flash-low", "muxer-av-orig-flash-mini",
+ "muxer-audio-orig-ogg-high", "muxer-audio-orig-ogg-medium", "muxer-audio-orig-ogg-low", "muxer-audio-orig-ogg-mini",
+ "muxer-audio-orig-mp3-high", "muxer-audio-orig-mp3-medium", "muxer-audio-orig-mp3-low", "muxer-audio-orig-mp3-mini",
+ "streamer-local1", "recorder-av", "recorder-audio" ],
+ "elemc00": [ "repeater-pub" ],
+ "elemc01": [ "streamer-pub1" ],
+ "elemc02": [ "streamer-pub2" ]
+ }
+ },
+ "inputs": {
+ "sdi-orig": {
+ "type": "decklink",
+ "master": true,
+ "properties": {
+ "resolution": "720p25",
+ "samplerate": 48000,
+ "device": 0,
+ "connection": 0,
+ "audio-input": 0,
+ "mode": 10
+ }
+ }
+ },
+ "muxes": {
+ "av-orig": {
+ "video": "sdi-orig:video",
+ "audio": "sdi-orig:audio",
+ "formats": {
+ "flash": [ "high", "medium", "low", "mini" ],
+ "webm": [ "high", "medium", "low", "mini" ]
+ }
+ },
+ "avr": {
+ "video": "sdi-orig:video",
+ "audio": "sdi-orig:audio",
+ "formats": {
+ "rec": [ "full" ]
+ }
+ },
+ "audio-orig": {
+ "audio": "sdi-orig:audio",
+ "formats": {
+ "ogg": [ "high", "medium", "low", "mini" ],
+ "mp3": [ "high", "medium", "low", "mini" ]
+ }
+ }
+ },
+ "streams": {
+ "local": {
+ "muxes": [ "av-orig", "audio-orig" ],
+ "type": "http", "count": 1,
+ "port": 8000,
+ "max-con": 100, "burst-on-connect": 5,
+ "hostname": "elevate-feed.spreadspace.org",
+ },
+ "public": {
+ "muxes": [ "av-orig", "audio-orig" ],
+ "type": "http", "count": 2,
+ "port": 8000, "interface": "localhost",
+ "max-bw": 290000000, "burst-on-connect": 5,
+ "hostname": "elevate-live%i.spreadspace.org", "repeater": True,
+ }
+ },
+ "records": {
+ "av": {
+ "muxes": {
+ "avr": { "format": "rec", "profile": "full" }
+ },
+ "directory": "/srv/elevate2017/",
+ "filename": "av-orig %Y-%m-%d %H-%M-%S"
+ },
+ "audio": {
+ "muxes": {
+ "audio-orig": { "format": "ogg", "profile": "high" }
+ },
+ "directory": "/srv/elevate2017/",
+ "filename": "audio-orig %Y-%m-%d %H-%M-%S"
+ }
+
+ }
+
+}
diff --git a/src/flufigut.py b/src/flufigut.py
index 4d8dbbe..72e9e7b 100755
--- a/src/flufigut.py
+++ b/src/flufigut.py
@@ -84,11 +84,13 @@ class Description:
class Porter:
- def __init__(self, name):
+ def __init__(self, name, interface, port):
self.name = name
+ self.interface = interface
+ self.port = port
self.socket_path = rand_string()
self.username = rand_string(size=12)
- self.password = rand_string(size=12)
+ self.password = rand_string(size=20)
class Planet:
@@ -266,17 +268,17 @@ class Planet:
#
# streams
- def __create_porter(self, comp_name_base, port, interface=None):
- porter_name = 'port-%s-%i' % (comp_name_base, port)
+ def __create_porter(self, stream, idx, port, interface=None):
+ comp_name = 'port-%s%i-%i' % (stream, idx + 1, port)
addr = '*:%i' % (port)
if interface:
- porter_name = 'port-%s-%s-%i' % (comp_name_base, interface, port)
+ comp_name = 'port-%s%i-%s-%i' % (stream, idx + 1, interface, port)
addr = '%s:%i' % (interface, port)
- porter = Porter(porter_name)
+ porter = Porter(comp_name, interface, port)
self.atmosphere[porter.name] = {
'type': "porter",
- 'desc': "Porter for %s on %s" % (comp_name_base, addr),
+ 'desc': "Porter for %s%i on %s" % (stream, idx + 1, addr),
'worker': None,
'properties': {
'port': port,
@@ -290,23 +292,77 @@ class Planet:
return porter
- def __generate_stream_instance(self, stream_name, stream, idx, globals):
- port = stream['port']
- comp_name_base = "%s%i" % (stream_name, idx + 1)
- porter = self.__create_porter(comp_name_base, port)
- porter_localdup = None
- if 'localdup' in stream:
- porter_localdump = self.__create_porter(comp_name_base, stream['localdup']['port'], "localhost")
+ def __generate_stream_mux_repeater(self, stream, mux, format, profile, feeder):
+ comp_name = 'repeater-%s-%s-%s-%s' % (stream, mux, format, profile)
+
+ if comp_name in self.flow['repeaters']:
+ return comp_name
+
+ self.flow['repeaters'][comp_name] = {
+ 'type': 'repeater',
+ 'desc': "repeater for %s (%s %s-%s)" % (stream, mux, format, profile),
+ 'worker': None,
+ 'feeder': feeder,
+ }
+ return comp_name
+
+ def __generate_stream_mux_instance(self, stream_name, stream, mux, format, profile, idx, cnt, porter, globals):
+ muxer_feed = 'muxer-%s-%s-%s' % (mux, format, profile)
+ feeder = muxer_feed
+ if 'repeater' in stream:
+ feeder = self.__generate_stream_mux_repeater(stream_name, mux, format, profile, muxer_feed)
+
+ comp_name = '%s-%s%i-%s-%s-%s' % (stream['type'], stream_name, idx + 1, mux, format, profile)
+ mount_point = '/%s-%s-%s.%s' % (mux, format, profile, globals['formats'][format]['muxer'])
+ if cnt > 1:
+ hostname = stream['hostname'] % (idx + 1)
+ if idx != 0:
+ hostname_next = stream['hostname'] % (idx)
+ else:
+ hostname_next = stream['hostname'] % (cnt)
+ else:
+ hostname = stream['hostname']
- def _generate_streams(self, streams, globals):
+ self.flow['streamers'][comp_name] = {
+ 'type': "%s-stream" % stream['type'],
+ 'desc': "%s streamer for %s %s-%s (part %i of %i in %s cluster)" % (stream['type'], mux, format, profile, idx + 1, cnt, stream_name),
+ 'worker': None,
+ 'feeder': feeder,
+ 'properties': {
+ 'description': globals['description'],
+ 'type': 'slave',
+ 'porter-socket-path': porter.socket_path,
+ 'porter-username': porter.username,
+ 'porter-password': porter.password,
+ 'mount-point': mount_point,
+ 'hostname': hostname,
+ 'port': porter.port,
+ }
+ }
+
+ # TODO: add support for max-con, max-bw and burst-on-connect!
+ # TODO: add stats!!
+
+ def __generate_stream_instance(self, stream_name, stream, idx, cnt, muxes, globals):
+ port = stream['port']
+ interface = None
+ if 'interface' in stream:
+ interface = stream['interface']
+ porter = self.__create_porter(stream_name, idx, port, interface)
+ for mux_name in stream['muxes']:
+ for format_name, format in muxes[mux_name]['formats'].items():
+ for profile_name in format:
+ self.__generate_stream_mux_instance(stream_name, stream, mux_name, format_name, profile_name,
+ idx, cnt, porter, globals)
+
+ def _generate_streams(self, streams, muxes, globals):
self.flow['repeaters'] = {}
self.flow['streamers'] = {}
for stream_name, stream in streams.items():
- streamer_cnt = stream['count']
- for idx in range(streamer_cnt):
- print(stream)
- self.__generate_stream_instance(stream_name, stream, idx, globals)
+ cnt = stream['count']
+ for idx in range(cnt):
+ self.__generate_stream_instance(stream_name, stream, idx, cnt, muxes, globals)
#
# records
@@ -338,7 +394,7 @@ class Planet:
def generate(self, desc):
self._generate_inputs(desc.inputs, desc.globals)
self._generate_muxes(desc.muxes, desc.inputs, desc.globals)
- self._generate_streams(desc.streams, desc.globals)
+ self._generate_streams(desc.streams, desc.muxes, desc.globals)
self._generate_records(desc.records, desc.globals)