diff options
-rwxr-xr-x | src/flufigut.py | 505 | ||||
-rw-r--r-- | src/test2.json | 44 | ||||
-rw-r--r-- | templates/example/decklink.xml | 2 | ||||
-rw-r--r-- | templates/example/soundcard.xml | 2 |
4 files changed, 287 insertions, 266 deletions
diff --git a/src/flufigut.py b/src/flufigut.py index 651d8fb..a3759a5 100755 --- a/src/flufigut.py +++ b/src/flufigut.py @@ -57,7 +57,7 @@ cf.close(); # globals = config['globals'] input = config['input'] -transcode = config['transcode'] +mux = config['mux'] stream = config['stream'] atmosphere = {} flow = {} @@ -76,277 +76,274 @@ for machine in globals['machines']: ### generate input components ################################### flow['input'] = {} -input_name = "" -input_type = "" -input_samplerate = 0 -input_resolution = "" - -if 'input' not in worker: - worker['input'] = -1 -else: - worker['input'] = 1 +master = 0 for source in input: - input_name = 'input-%s' % source - input_type = input[source]['type'] - flow['input'][input_name] = { - 'type': input[source]['source'], - 'desc': "capture raw %s from %s" % (input[source]['type'], source), - 'worker': 'input', + name = 'input-%s' % source + if name not in worker: + worker[name] = -1 + else: + worker[name] = 1 + flow['input'][name] = { + 'type': input[source]['type'], + 'desc': "capture raw data from %s" % (source), + 'worker': name, + 'master': input[source]['master'], 'properties': {}, } + if input[source]['master']: + master += 1 properties = input[source]['properties'] for property in properties.keys(): if property == 'resolution': - flow['input'][input_name]['properties']['width'] = globals['resolutions'][properties[property]]['width'] - flow['input'][input_name]['properties']['height'] = globals['resolutions'][properties[property]]['height'] - flow['input'][input_name]['properties']['framerate'] = globals['resolutions'][properties[property]]['rate'] - input_resolution = properties[property] + flow['input'][name]['properties']['width'] = globals['resolutions'][properties[property]]['width'] + flow['input'][name]['properties']['height'] = globals['resolutions'][properties[property]]['height'] + flow['input'][name]['properties']['framerate'] = globals['resolutions'][properties[property]]['rate'] else: - flow['input'][input_name]['properties'][property] = properties[property] - if property == 'samplerate': - input_samplerate = properties[property] - - break # until now only one input source can be used - -if input_name == "": - raise SystemExit("no input defined!") - -samplerates = [ ] -resolutions = [ ] -for format in transcode.keys(): - if 'samplerate' in globals['formats'][format]: - samplerate = globals['formats'][format]['samplerate'] - if samplerate not in samplerates: - if input_samplerate != samplerate: - samplerates.append(samplerate) - for profile in transcode[format]: - if 'video' in globals['profiles'][profile]: - if input_resolution == "": - raise SystemExit("format definition needs video but no video input given") - resolution = globals['profiles'][profile]['video'] - if resolution not in resolutions: - if input_resolution != resolution: - if globals['resolutions'][resolution]['rate'] != globals['resolutions'][input_resolution]['rate']: - raise SystemExit("ERROR: video rate conversion is not yet supported!!!") - resolutions.append(resolution) - -for resolution in resolutions: - if 'resize' not in worker: - worker['resize'] = -1 - else: - worker['resize'] = 1 - if input_type == 'av': - feeder = '%s:%s' % (input_name, "video") - elif input_type == 'vo': - feeder = '%s' % (input_name) - else: - raise SystemExit("format definition needs video but no video input given") - flow['input']['resize-%s' % resolution] = { - 'type': 'video-resize', - 'desc': "resize video to %sx%s" % (globals['resolutions'][resolution]['width'], globals['resolutions'][resolution]['height']), - 'worker': 'resize', - 'feeder': feeder, - 'properties': { - 'width': globals['resolutions'][resolution]['width'], - 'height': globals['resolutions'][resolution]['height'], - }, - } - -for samplerate in samplerates: - if 'resample' not in worker: - worker['resample'] = -1 - else: - worker['resample'] = 1 - - if input_type == 'av': - feeder = '%s:%s' % (input_name, "audio") - elif input_type == 'ao': - feeder = '%s' % (input_name) - else: - raise SystemExit("format definition needs audio but no audio input given") - flow['input']['resample-%s' % samplerate] = { - 'type': 'audio-resample', - 'desc': "resample audio to %s Hz" % samplerate, - 'worker': 'resample', - 'feeder': feeder, - 'properties': { - 'samplerate': samplerate, - }, - } + flow['input'][name]['properties'][property] = properties[property] + +if master == 0: + raise SystemExit("You have not configured any master clock device!") +elif master > 1: + raise SystemExit("You have configured multiple master clock devices!") + + + +# samplerates = [ ] +# resolutions = [ ] +# for format in transcode.keys(): +# if 'samplerate' in globals['formats'][format]: +# samplerate = globals['formats'][format]['samplerate'] +# if samplerate not in samplerates: +# if input_samplerate != samplerate: +# samplerates.append(samplerate) +# for profile in transcode[format]: +# if 'video' in globals['profiles'][profile]: +# if input_resolution == "": +# raise SystemExit("format definition needs video but no video input given") +# resolution = globals['profiles'][profile]['video'] +# if resolution not in resolutions: +# if input_resolution != resolution: +# if globals['resolutions'][resolution]['rate'] != globals['resolutions'][input_resolution]['rate']: +# raise SystemExit("ERROR: video rate conversion is not yet supported!!!") +# resolutions.append(resolution) + +# for resolution in resolutions: +# if 'resize' not in worker: +# worker['resize'] = -1 +# else: +# worker['resize'] = 1 +# if input_type == 'av': +# feeder = '%s:%s' % (input_name, "video") +# elif input_type == 'vo': +# feeder = '%s' % (input_name) +# else: +# raise SystemExit("format definition needs video but no video input given") +# flow['input']['resize-%s' % resolution] = { +# 'type': 'video-resize', +# 'desc': "resize video to %sx%s" % (globals['resolutions'][resolution]['width'], globals['resolutions'][resolution]['height']), +# 'worker': 'resize', +# 'feeder': feeder, +# 'properties': { +# 'width': globals['resolutions'][resolution]['width'], +# 'height': globals['resolutions'][resolution]['height'], +# }, +# } + +# for samplerate in samplerates: +# if 'resample' not in worker: +# worker['resample'] = -1 +# else: +# worker['resample'] = 1 + +# if input_type == 'av': +# feeder = '%s:%s' % (input_name, "audio") +# elif input_type == 'ao': +# feeder = '%s' % (input_name) +# else: +# raise SystemExit("format definition needs audio but no audio input given") +# flow['input']['resample-%s' % samplerate] = { +# 'type': 'audio-resample', +# 'desc': "resample audio to %s Hz" % samplerate, +# 'worker': 'resample', +# 'feeder': feeder, +# 'properties': { +# 'samplerate': samplerate, +# }, +# } ### generate encoder and muxer components ####################### flow['encoder_video'] = {} flow['encoder_audio'] = {} flow['muxer'] = {} -for format in transcode.keys(): - for profile in transcode[format]: - video_encoder = 'none' - if 'video' in globals['formats'][format]: - encoder = globals['formats'][format]['video'] - resolution = globals['profiles'][profile]['video'] - bitrate = globals['bitrates'][encoder][resolution] - if resolution != input_resolution: - feeder = 'resize-%s' % resolution - else: - if input_type == 'av': - feeder = '%s:%s' % (input_name, "video") - elif input_type == 'vo': - feeder = '%s' % (input_name) - else: - raise SystemExit("format definition needs audio but no audio input given") - video_encoder = 'encode-%s-%s' % (encoder, resolution) - if video_encoder not in flow['encoder_video'].keys(): - worker_name = 'encoder-%s-%s' % (encoder, resolution) - if worker_name not in worker: - worker[worker_name] = -1 - else: - worker[worker_name] = 1 - flow['encoder_video'][video_encoder] = { - 'type': '%s-encode' % encoder, - 'desc': "%s encoder for %sx%s" % (encoder, globals['resolutions'][resolution]['width'], globals['resolutions'][resolution]['height']), - 'worker': worker_name, - 'feeder': feeder, - 'properties': { - 'bitrate': bitrate, - }, - } - - audio_encoder = 'none' - if 'audio' in globals['formats'][format]: - encoder = globals['formats'][format]['audio'] - bitrate = globals['profiles'][profile]['audio'] - if 'samplerate' in globals['formats'][format]: - samplerate = globals['formats'][format]['samplerate'] - else: - samplerate = input_samplerate - - if samplerate != input_samplerate: - feeder = 'resample-%s' % samplerate - else: - if input_type == 'av': - feeder = '%s:%s' % (input_name, "audio") - elif input_type == 'ao': - feeder = '%s' % (input_name) - else: - raise SystemExit("format definition needs audio but no audio input given") - audio_encoder = 'encode-%s-%i-%i' % (encoder, bitrate, samplerate) - if audio_encoder not in flow['encoder_audio']: - worker_name = 'encoder-%s-%s' % (encoder, bitrate) - if worker_name not in worker: - worker[worker_name] = -1 - else: - worker[worker_name] = 1 - flow['encoder_audio'][audio_encoder] = { - 'type': '%s-encode' % encoder, - 'desc': "%s encoder for %i kbit/s @ %i Hz" % (encoder, bitrate, samplerate), - 'worker': worker_name, - 'feeder': feeder, - 'properties': { - 'bitrate': bitrate, - }, - } - - muxer = globals['formats'][format]['muxer'] - worker_name = 'muxer-%s-%s' % (format, profile) - if worker_name not in worker: - worker[worker_name] = -1 - else: - worker[worker_name] = 1 - flow['muxer']['muxer-%s-%s' % (format, profile)] = { - 'type': '%s-mux' % muxer, - 'desc': "%s muxer profile %s" % (format, profile), - 'worker': worker_name, - 'feeder_audio': audio_encoder, - 'feeder_video': video_encoder, - 'properties': {}, - } +# for format in transcode.keys(): +# for profile in transcode[format]: +# video_encoder = 'none' +# if 'video' in globals['formats'][format]: +# encoder = globals['formats'][format]['video'] +# resolution = globals['profiles'][profile]['video'] +# bitrate = globals['bitrates'][encoder][resolution] +# if resolution != input_resolution: +# feeder = 'resize-%s' % resolution +# else: +# if input_type == 'av': +# feeder = '%s:%s' % (input_name, "video") +# elif input_type == 'vo': +# feeder = '%s' % (input_name) +# else: +# raise SystemExit("format definition needs audio but no audio input given") +# video_encoder = 'encode-%s-%s' % (encoder, resolution) +# if video_encoder not in flow['encoder_video'].keys(): +# worker_name = 'encoder-%s-%s' % (encoder, resolution) +# if worker_name not in worker: +# worker[worker_name] = -1 +# else: +# worker[worker_name] = 1 +# flow['encoder_video'][video_encoder] = { +# 'type': '%s-encode' % encoder, +# 'desc': "%s encoder for %sx%s" % (encoder, globals['resolutions'][resolution]['width'], globals['resolutions'][resolution]['height']), +# 'worker': worker_name, +# 'feeder': feeder, +# 'properties': { +# 'bitrate': bitrate, +# }, +# } + +# audio_encoder = 'none' +# if 'audio' in globals['formats'][format]: +# encoder = globals['formats'][format]['audio'] +# bitrate = globals['profiles'][profile]['audio'] +# if 'samplerate' in globals['formats'][format]: +# samplerate = globals['formats'][format]['samplerate'] +# else: +# samplerate = input_samplerate + +# if samplerate != input_samplerate: +# feeder = 'resample-%s' % samplerate +# else: +# if input_type == 'av': +# feeder = '%s:%s' % (input_name, "audio") +# elif input_type == 'ao': +# feeder = '%s' % (input_name) +# else: +# raise SystemExit("format definition needs audio but no audio input given") +# audio_encoder = 'encode-%s-%i-%i' % (encoder, bitrate, samplerate) +# if audio_encoder not in flow['encoder_audio']: +# worker_name = 'encoder-%s-%s' % (encoder, bitrate) +# if worker_name not in worker: +# worker[worker_name] = -1 +# else: +# worker[worker_name] = 1 +# flow['encoder_audio'][audio_encoder] = { +# 'type': '%s-encode' % encoder, +# 'desc': "%s encoder for %i kbit/s @ %i Hz" % (encoder, bitrate, samplerate), +# 'worker': worker_name, +# 'feeder': feeder, +# 'properties': { +# 'bitrate': bitrate, +# }, +# } + +# muxer = globals['formats'][format]['muxer'] +# worker_name = 'muxer-%s-%s' % (format, profile) +# if worker_name not in worker: +# worker[worker_name] = -1 +# else: +# worker[worker_name] = 1 +# flow['muxer']['muxer-%s-%s' % (format, profile)] = { +# 'type': '%s-mux' % muxer, +# 'desc': "%s muxer profile %s" % (format, profile), +# 'worker': worker_name, +# 'feeder_audio': audio_encoder, +# 'feeder_video': video_encoder, +# 'properties': {}, +# } ### generate streamer components ################################ flow['streamer'] = {} -for cluster in stream.keys(): - streamer_cnt = stream[cluster]['count'] - port = stream[cluster]['port'] - for idx in range(streamer_cnt): - stream_worker = '%s%i'%(cluster, idx+1) - for machine in globals['machines'].keys(): - if stream_worker in globals['machines'][machine]: - if machine in machines: - if 'porter' in machines[machine]: - if port in machines[machine]['porter']: - raise SystemExit("ERROR: porter cannot be created because machine '%s' already uses port %i" % (machine, port)) - else: - machines[machine]['porter'] = {} - else: - machines[machine] = { 'porter': {} } - - machines[machine]['porter'][port] = { - 'socket-path': "porter-%s"%(rand_string()), - 'username': rand_string(size=12), - 'password': rand_string(size=12), - } - - if stream_worker not in worker: - worker[stream_worker] = -1 - else: - worker[stream_worker] = 1 - atmosphere['porter-%s-%i'%(machine, port)] = { - 'type': "porter", - 'desc': "Porter for %s on port %i"%(machine, port), - 'worker': stream_worker, - 'properties': { - 'port': port, - 'socket-path': machines[machine]['porter'][port]['socket-path'], - 'username': machines[machine]['porter'][port]['username'], - 'password': machines[machine]['porter'][port]['password'], - }, - } - - for format in stream[cluster]['formats']: - for profile in transcode[format]: - feeder = 'muxer-%s-%s' % (format, profile) - name = '%s-%s%i-%s-%s' % (stream[cluster]['type'], cluster, idx+1, format, profile) - mount_point = '/%s-%s.%s' % (format, profile, globals['formats'][format]['muxer']) - if streamer_cnt > 1: - hostname = "%s.%s" % (stream[cluster]['hostname'] % (idx+1), globals['domain']) - if idx != 0: - hostname_next = "%s.%s" % (stream[cluster]['hostname'] % (idx), globals['domain']) - else: - hostname_next = "%s.%s" % (stream[cluster]['hostname'] % (streamer_cnt), globals['domain']) - else: - hostname = "%s.%s" % (stream[cluster]['hostname'], globals['domain']) - flow['streamer'][name] = { - 'type': "%s-stream" % stream[cluster]['type'], - 'desc': "%s streamer for %s-%s (part %i of %s cluster)" % (stream[cluster]['type'], format, profile, idx+1, cluster), - 'worker': stream_worker, - 'feeder': feeder, - 'rrd_clients' : "%s/%s_clients.rrd" % (globals['rrd-dir'], name), - 'rrd_bytes' : "%s/%s_bytes.rrd" % (globals['rrd-dir'], name), - 'properties': { - 'description': globals['description'], - 'type': 'slave', - 'porter-socket-path': machines[machine]['porter'][port]['socket-path'], - 'porter-username': machines[machine]['porter'][port]['username'], - 'porter-password': machines[machine]['porter'][port]['password'], - 'mount-point': mount_point, - 'hostname': hostname, - 'port': port, - } - } - for prop in stream[cluster]: - if prop == 'max-con': - flow['streamer'][name]['properties']['client-limit'] = stream[cluster][prop] - if streamer_cnt > 1: - flow['streamer'][name]['properties']['redirect-on-overflow'] = "http://%s:%i%s" % (hostname_next, port, mount_point) - if prop == 'max-bw': - flow['streamer'][name]['properties']['bandwidth-limit'] = stream[cluster][prop] - if streamer_cnt > 1: - flow['streamer'][name]['properties']['redirect-on-overflow'] = "http://%s:%i%s" % (hostname_next, port, mount_point) - if prop == 'burst-on-connect': - flow['streamer'][name]['properties']['burst-on-connect'] = 'true' - flow['streamer'][name]['properties']['burst-time'] = stream[cluster][prop] +# for cluster in stream.keys(): +# streamer_cnt = stream[cluster]['count'] +# port = stream[cluster]['port'] +# for idx in range(streamer_cnt): +# stream_worker = '%s%i'%(cluster, idx+1) +# for machine in globals['machines'].keys(): +# if stream_worker in globals['machines'][machine]: +# if machine in machines: +# if 'porter' in machines[machine]: +# if port in machines[machine]['porter']: +# raise SystemExit("ERROR: porter cannot be created because machine '%s' already uses port %i" % (machine, port)) +# else: +# machines[machine]['porter'] = {} +# else: +# machines[machine] = { 'porter': {} } + +# machines[machine]['porter'][port] = { +# 'socket-path': "porter-%s"%(rand_string()), +# 'username': rand_string(size=12), +# 'password': rand_string(size=12), +# } + +# if stream_worker not in worker: +# worker[stream_worker] = -1 +# else: +# worker[stream_worker] = 1 +# atmosphere['porter-%s-%i'%(machine, port)] = { +# 'type': "porter", +# 'desc': "Porter for %s on port %i"%(machine, port), +# 'worker': stream_worker, +# 'properties': { +# 'port': port, +# 'socket-path': machines[machine]['porter'][port]['socket-path'], +# 'username': machines[machine]['porter'][port]['username'], +# 'password': machines[machine]['porter'][port]['password'], +# }, +# } + +# for format in stream[cluster]['formats']: +# for profile in transcode[format]: +# feeder = 'muxer-%s-%s' % (format, profile) +# name = '%s-%s%i-%s-%s' % (stream[cluster]['type'], cluster, idx+1, format, profile) +# mount_point = '/%s-%s.%s' % (format, profile, globals['formats'][format]['muxer']) +# if streamer_cnt > 1: +# hostname = "%s.%s" % (stream[cluster]['hostname'] % (idx+1), globals['domain']) +# if idx != 0: +# hostname_next = "%s.%s" % (stream[cluster]['hostname'] % (idx), globals['domain']) +# else: +# hostname_next = "%s.%s" % (stream[cluster]['hostname'] % (streamer_cnt), globals['domain']) +# else: +# hostname = "%s.%s" % (stream[cluster]['hostname'], globals['domain']) +# flow['streamer'][name] = { +# 'type': "%s-stream" % stream[cluster]['type'], +# 'desc': "%s streamer for %s-%s (part %i of %s cluster)" % (stream[cluster]['type'], format, profile, idx+1, cluster), +# 'worker': stream_worker, +# 'feeder': feeder, +# 'rrd_clients' : "%s/%s_clients.rrd" % (globals['rrd-dir'], name), +# 'rrd_bytes' : "%s/%s_bytes.rrd" % (globals['rrd-dir'], name), +# 'properties': { +# 'description': globals['description'], +# 'type': 'slave', +# 'porter-socket-path': machines[machine]['porter'][port]['socket-path'], +# 'porter-username': machines[machine]['porter'][port]['username'], +# 'porter-password': machines[machine]['porter'][port]['password'], +# 'mount-point': mount_point, +# 'hostname': hostname, +# 'port': port, +# } +# } +# for prop in stream[cluster]: +# if prop == 'max-con': +# flow['streamer'][name]['properties']['client-limit'] = stream[cluster][prop] +# if streamer_cnt > 1: +# flow['streamer'][name]['properties']['redirect-on-overflow'] = "http://%s:%i%s" % (hostname_next, port, mount_point) +# if prop == 'max-bw': +# flow['streamer'][name]['properties']['bandwidth-limit'] = stream[cluster][prop] +# if streamer_cnt > 1: +# flow['streamer'][name]['properties']['redirect-on-overflow'] = "http://%s:%i%s" % (hostname_next, port, mount_point) +# if prop == 'burst-on-connect': +# flow['streamer'][name]['properties']['burst-on-connect'] = 'true' +# flow['streamer'][name]['properties']['burst-time'] = stream[cluster][prop] ### sanity checks, cont'd ####################################### diff --git a/src/test2.json b/src/test2.json index f3bd642..5e78dc9 100644 --- a/src/test2.json +++ b/src/test2.json @@ -28,18 +28,18 @@ }, "bitrates": { "h264": { "720p25": 2048, "480p25": 1024, "360p25": 600 }, - "vp8": { "720p25": 2048, "480p25": 1024, "360p25": 600 }, + "vp8": { "720p25": 2048, "480p25": 1024, "360p25": 600 } }, "machines": { - "calypso": [ "input", "resample", "resize", "encoder-vorbis-160", "encoder-aac-160", "encoder-aac-128", "encoder-aac-96", + "calypso": [ "input-sdi-av", "input-alsa", "resample", "resize", "encoder-vorbis-160", "encoder-aac-160", "encoder-aac-128", "encoder-aac-96", "muxer-flash-high", "muxer-flash-medium", "muxer-flash-low", "muxer-ogg-high", "streamer-local1" ], "telesto": [ "encoder-h264-720p25", "encoder-h264-480p25", "encoder-h264-360p25" ] } }, "input": { "sdi-av": { - "type": "av", - "source": "decklink", + "type": "decklink", + "master": true, "properties": { "resolution": "720p25", "samplerate": 48000, @@ -50,8 +50,8 @@ } }, "alsa": { - "type": "ao", - "source": "soundcard", + "type": "soundcard", + "master": false, "properties": { "samplerate": 48000, "depth": 16, @@ -59,13 +59,37 @@ } } }, - "transcode": { - "flash": [ "high", "medium", "low" ], - "ogg": [ "high" ] + "mux": { + "av-orig": { + "video": "sdi-av:video", + "audio": "sdi-av:audio", + "formats": { + "flash": [ "high", "medium", "low" ] + } + }, + "av-en": { + "video": "sdi-av:video", + "audio": "alsa", + "formats": { + "flash": [ "high", "medium", "low" ] + } + }, + "audio": { + "audio": "sdi-av:audio", + "formats": { + "ogg": [ "high" ] + } + }, + "audio-en": { + "audio": "alsa", + "formats": { + "ogg": [ "high" ] + } + } }, "stream": { "streamer-local": { - "formats": [ "flash", "ogg" ], + "muxes": [ "av-orig", "av-en", "audio", "audio-en" ], "type": "http", "count": 1, "port": 8000, "max-con": 5, "burst-on-connect": 5, "hostname": "elevate-feed" diff --git a/templates/example/decklink.xml b/templates/example/decklink.xml index 1630d95..5eb6a2a 100644 --- a/templates/example/decklink.xml +++ b/templates/example/decklink.xml @@ -9,5 +9,5 @@ <property name="{{ name }}">{{ value }}</property> %% endif %% endfor - <clock-master>true</clock-master> + <clock-master>{{ component.master }}</clock-master> </component> diff --git a/templates/example/soundcard.xml b/templates/example/soundcard.xml index da419aa..07f4c99 100644 --- a/templates/example/soundcard.xml +++ b/templates/example/soundcard.xml @@ -7,5 +7,5 @@ %% for name, value in component.properties.items() <property name="{{ name }}">{{ value }}</property> %% endfor - <clock-master>true</clock-master> + <clock-master>{{ component.master }}</clock-master> </component> |