pipe uncompressed stream, add gen_input function, clean function

This commit is contained in:
jb-alvarado 2018-02-19 11:12:13 +01:00
parent bac14b82d4
commit 056009933f
2 changed files with 163 additions and 153 deletions

View File

@ -40,15 +40,11 @@ log_level = INFO
# output settings for the pre-compression # output settings for the pre-compression
# all clips get prepared in that way, # all clips get prepared in that way,
# so the input for the final compression is unique # so the input for the final compression is unique
# it produce a mpeg2 ts stream # it produce a uncompressed avi stream
# bitrate is in kbit/s
[PRE_COMPRESS] [PRE_COMPRESS]
width = 1024 width = 1024
height = 576 height = 576
fps = 25 fps = 25
v_bitrate = 15000
a_bitrate = 256
a_sample = 44100 a_sample = 44100
@ -60,7 +56,8 @@ a_sample = 44100
# strings in playlist must have ampersan (&) as: & # strings in playlist must have ampersan (&) as: &
# day_start means at witch hour starts the day, as integer # day_start means at witch hour starts the day, as integer
# filler_path and filler_clip are for the GUI only at the moment # filler_path are for the GUI only at the moment
# filler_clip get handle different, when a new length needs to calculate
[PLAYLIST] [PLAYLIST]
playlist_path = /playlists playlist_path = /playlists
clips_root = /ADtvMedia clips_root = /ADtvMedia
@ -77,7 +74,10 @@ day_start = 6
# buffer_length: length in seconds of the buffer # buffer_length: length in seconds of the buffer
# this is the time what the playout have, to change from one clip to the next # this is the time what the playout have, to change from one clip to the next
# be liberal with this value but dont exaggerate # be liberal with this value but dont exaggerate
# buffer size gets calculate with: (v_bitrate + a_bitrate) * 0.125 * buffer_length # buffer size gets calculate with:
# (width x height * 3 / 2 * fps * buffer_length / 1024) + ((a_sample * 16 * 2 * buffer_length) / 8 / 1024)
# uncompressed audio and video (yuv420p), solution is in KB
# buffer_cli: the prefert buffer tool, needs to be installed on your system # buffer_cli: the prefert buffer tool, needs to be installed on your system
# buffer_cmd: need to end with the buffer size command, full command would look: # buffer_cmd: need to end with the buffer size command, full command would look:

View File

@ -66,15 +66,13 @@ _pre_comp = SimpleNamespace(
aspect=cfg.getfloat('PRE_COMPRESS', 'width') / aspect=cfg.getfloat('PRE_COMPRESS', 'width') /
cfg.getfloat('PRE_COMPRESS', 'height'), cfg.getfloat('PRE_COMPRESS', 'height'),
fps=cfg.getint('PRE_COMPRESS', 'fps'), fps=cfg.getint('PRE_COMPRESS', 'fps'),
v_bitrate=cfg.getint('PRE_COMPRESS', 'v_bitrate'), a_sample=cfg.getint('PRE_COMPRESS', 'a_sample')
v_bufsize=cfg.getint('PRE_COMPRESS', 'v_bitrate'),
a_bitrate=cfg.getint('PRE_COMPRESS', 'a_bitrate'),
a_sample=cfg.getint('PRE_COMPRESS', 'a_sample'),
) )
_playlist = SimpleNamespace( _playlist = SimpleNamespace(
path=cfg.get('PLAYLIST', 'playlist_path'), path=cfg.get('PLAYLIST', 'playlist_path'),
start=cfg.getint('PLAYLIST', 'day_start') start=cfg.getint('PLAYLIST', 'day_start'),
filler=cfg.get('PLAYLIST', 'filler_clip')
) )
_buffer = SimpleNamespace( _buffer = SimpleNamespace(
@ -94,7 +92,7 @@ _playout = SimpleNamespace(
# set logo filtergraph # set logo filtergraph
if path.exists(cfg.get('OUT', 'logo')): if path.exists(cfg.get('OUT', 'logo')):
_playout.logo = ['-thread_queue_size', '512', '-i', cfg.get('OUT', 'logo')] _playout.logo = ['-thread_queue_size', '16', '-i', cfg.get('OUT', 'logo')]
_playout.filter = [ _playout.filter = [
'-filter_complex', '[0:v][1:v]' + cfg.get('OUT', 'logo_o') + '[o]', '-filter_complex', '[0:v][1:v]' + cfg.get('OUT', 'logo_o') + '[o]',
'-map', '[o]', '-map', '0:a' '-map', '[o]', '-map', '0:a'
@ -157,7 +155,9 @@ def get_time(time_format):
if time_format == 'hour': if time_format == 'hour':
return t.hour return t.hour
elif time_format == 'full_sec': elif time_format == 'full_sec':
return t.hour * 3600 + t.minute * 60 + t.second sec = float(t.hour * 3600 + t.minute * 60 + t.second)
micro = float(t.microsecond) / 1000000
return sec + micro
else: else:
return t.strftime("%H:%M:%S") return t.strftime("%H:%M:%S")
@ -192,7 +192,9 @@ def mail_or_log(message, time, path):
# calculating the size for the buffer in KB # calculating the size for the buffer in KB
def calc_buffer_size(): def calc_buffer_size():
return (_pre_comp.v_bitrate + _pre_comp.a_bitrate) * 0.125 * _buffer.length v_size = _pre_comp.w * _pre_comp.h * 3 / 2 * _pre_comp.fps * _buffer.length
a_size = (_pre_comp.a_sample * 16 * 2 * _buffer.length) / 8
return (v_size + a_size) / 1024
# check if processes a well # check if processes a well
@ -252,62 +254,66 @@ def gen_dummy(duration):
] ]
# prepare input clip # when source path exist, generate input with seek and out time
def prepare_input(src, duration, seek, out): # when path not exist, get dummy clip
def src_or_dummy(src, duration, seek, out):
if check_file_exist(src): if check_file_exist(src):
if seek > 0.0 or out < duration: if seek > 0.0 or out < duration:
src_cmd = seek_in_cut_end(src, duration, seek, out) return seek_in_cut_end(src, duration, seek, out)
else: else:
src_cmd = ['-i', src] return ['-i', src]
else: else:
if seek > 0.0: return gen_dummy(out - seek)
duration = duration - seek
if out < duration:
duration = duration - out
src_cmd = gen_dummy(duration)
return src_cmd
# last clip can be a filler # prepare input clip
# so we get the IN point and calculate the new duration # check begin and length from clip
# if the new duration is smaller then 6 sec put a blank clip # return clip only if we are in 24 hours time range
# we have to validate here to, that a last clip exists in the playlist def gen_input(src, begin, duration, seek, out, last):
def prepare_last_clip(clip_nodes): test_time = 86400.0
if clip_nodes: start_time = float(_playlist.start * 3600)
last_node = clip_nodes[-1]
src = last_node.get('src') if begin + out - seek > test_time:
begin = is_float(last_node.get('begin'), get_time('full_sec'), True) begin -= start_time
duration = is_float(last_node.get('dur'), 300, True)
seek = is_float(last_node.get('in'), 0, True) playlist_length = begin + out - seek
out = is_float(last_node.get('out'), 300, True)
first = False if playlist_length <= test_time and not last:
last = True # when we are in the 24 houre range, get the clip
else: return src_or_dummy(src, duration, seek, out)
src = None elif playlist_length < test_time and last:
begin = get_time('full_sec') # when last clip is passed and we still have too much time left
duration = 300.0 diff = test_time - playlist_length
seek = 0.0
out = duration if duration < diff:
first = True
last = False
mail_or_log( mail_or_log(
'Playlist has no valid entries!', 'Last clip is not long enough', get_time(None),
get_time(None), get_date(True) str(diff - duration) + ' seconds are missing.'
) )
src_cmd = src_or_dummy(src, duration, 0, duration)
tmp_dur = out - seek
if tmp_dur > 6.0:
if check_file_exist(src):
src_cmd = seek_in_cut_end(src, duration, seek, out)
else: else:
src_cmd = gen_dummy(tmp_dur) if src == _playlist.filler:
elif tmp_dur > 1.0: src_cmd = src_or_dummy(src, duration, duration - diff, out)
src_cmd = gen_dummy(tmp_dur) else:
src_cmd = src_or_dummy(src, duration, 0, duration - diff)
else:
# when we over the 24 hours range,
# calculate time and try to correct them
diff = playlist_length - test_time
new_len = out - seek - diff
if new_len > 6.0:
if src == _playlist.filler:
# when filler is something like a clock,
# is better to start the clip later, to play until end
src_cmd = src_or_dummy(src, duration, seek + diff, out)
else:
src_cmd = src_or_dummy(src, duration, seek, out - diff)
elif new_len > 1.0:
src_cmd = gen_dummy(new_len)
else: else:
src_cmd = None src_cmd = None
return src_cmd, begin, first, last return src_cmd
# test if value is float # test if value is float
@ -322,9 +328,10 @@ def is_float(value, text, convert):
return text return text
# check all variables in xml playlist # validate xml values in new Thread
# and test if file path exist # and test if file path exist
def validate_xml(xml_nodes): def validate_thread(clip_nodes):
def check_xml(xml_nodes):
error = '' error = ''
for xml_node in xml_nodes: for xml_node in xml_nodes:
@ -348,6 +355,24 @@ def validate_xml(xml_nodes):
get_time(None), error get_time(None), error
) )
validate = Thread(name='check_xml', target=check_xml, args=(clip_nodes,))
validate.daemon = True
validate.start()
def exeption(last_time, message, path):
src_cmd = gen_dummy(300)
# there is still material in the buffer,
# so we have to calculate the right seek time for the new playlist
if last_time > 86400:
last_time -= 86400
time_diff = last_time - get_time('full_sec')
last_time = get_time('full_sec')
mail_or_log(message, get_time(None), path)
return src_cmd, last_time, time_diff
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# main functions # main functions
@ -361,23 +386,10 @@ def iter_src_commands():
last_mod_time = 0.0 last_mod_time = 0.0
time_diff = 0.0 time_diff = 0.0
first = True first = True
last = False
time_difference = 0.0
while True:
# switch playlist after last clip from day befor
if last:
if time_difference > float(_buffer.length):
# wait to sync time
wait = time_difference - float(_buffer.length)
logger.info('Wait for: ' + str(wait) + ' seconds.')
sleep(wait)
list_date = get_date(False)
last = False
else:
list_date = get_date(True) list_date = get_date(True)
year, month, _day = re.split('-', list_date) while True:
year, month, day = re.split('-', list_date)
xml_path = path.join(_playlist.path, year, month, list_date + '.xml') xml_path = path.join(_playlist.path, year, month, list_date + '.xml')
if check_file_exist(xml_path): if check_file_exist(xml_path):
@ -388,74 +400,77 @@ def iter_src_commands():
clip_nodes = xml_root.findall('body/video') clip_nodes = xml_root.findall('body/video')
last_mod_time = mod_time last_mod_time = mod_time
logger.info('open: ' + xml_path) logger.info('open: ' + xml_path)
validate_thread(clip_nodes)
# validate xml values in new Thread last_node = clip_nodes[-1]
validate_thread = Thread(
name='validate_xml', target=validate_xml, args=(
clip_nodes,
)
)
validate_thread.daemon = True
validate_thread.start()
# all clips in playlist except last one # all clips in playlist except last one
for clip_node in clip_nodes[:-1]: for clip_node in clip_nodes:
src = clip_node.get('src') src = clip_node.get('src')
begin = is_float(clip_node.get('begin'), last_time, True) begin = is_float(clip_node.get('begin'), last_time, True)
duration = is_float(clip_node.get('dur'), 300, True) duration = is_float(clip_node.get('dur'), 300, True)
seek = is_float(clip_node.get('in'), 0, True) seek = is_float(clip_node.get('in'), 0, True)
out = is_float(clip_node.get('out'), 300, True) out = is_float(clip_node.get('out'), 300, True)
if first:
# first time we end up here # first time we end up here
if last_time < begin + duration: if first and last_time + time_diff < begin + duration:
# calculate seek time # calculate seek time
init_seek = last_time - begin + seek + time_diff seek = last_time - begin + seek + time_diff
src_cmd = prepare_input(src, duration, init_seek, out) src_cmd = gen_input(
src, begin, duration, seek, out, False
)
time_diff = 0.0 time_diff = 0.0
first = False first = False
last_time = begin last_time = begin
break break
else: elif last_time < begin:
if last_time < begin: if clip_node == last_node:
src_cmd = prepare_input(src, duration, seek, out) # after last item is loaded,
last_time = begin # set right values for new playlist
break list_date = get_date(False)
else:
# last clip in playlist
src_cmd, begin, first, last = prepare_last_clip(clip_nodes)
if last_time > 86400:
begin -= 86400.0
# calculate real time in buffer
time_difference = begin - get_time('full_sec')
last_time = float(_playlist.start * 3600 - 5) last_time = float(_playlist.start * 3600 - 5)
list_date = get_date(True)
last_mod_time = 0.0 last_mod_time = 0.0
last = True
else:
last_time = begin
last = False
t_dist = begin - get_time('full_sec')
if 0 <= get_time('full_sec') < _playlist.start * 3600:
t_dist -= 86400
# check that we are in tolerance time
if not _buffer.length - 8 < t_dist < _buffer.length + 8:
mail_or_log(
'Playlist is not sync!', get_time(None),
str(t_dist) + ' seconds async.'
)
src_cmd = gen_input(
src, begin, duration, seek, out, last
)
break
else:
# when playlist exist but is empty, or not long enough,
# generate dummy and send log
src_cmd, last_time, time_diff = exeption(
last_time, 'Playlist is not valid!', xml_path
)
first = True
last_mod_time = 0.0
else: else:
# when we have no playlist for the current day, # when we have no playlist for the current day,
# then we generate a black clip # then we generate a black clip
# and calculate the seek in time, for when the playlist comes back # and calculate the seek in time, for when the playlist comes back
src_cmd = gen_dummy(300) src_cmd, last_time, time_diff = exeption(
last_time += 300 last_time, 'Playlist not exist:', xml_path
last_mod_time = 0.0 )
mail_or_log('Playlist not exist:', get_time(None), xml_path)
# there is still material in the buffer,
# so we have to calculate the right seek time for the new playlist
# time_diff: is the real time what we have in buffer
if last_time > 86400:
time_val = last_time - 86400
else:
time_val = last_time
time_diff = time_val - get_time('full_sec')
first = True first = True
last_mod_time = 0.0
if src_cmd is not None: if src_cmd is not None:
yield src_cmd, last_time yield src_cmd, last_time
@ -483,14 +498,10 @@ def play_clips(out_file, iter_src_commands):
'-aspect', str(_pre_comp.aspect), '-aspect', str(_pre_comp.aspect),
'-pix_fmt', 'yuv420p', '-r', str(_pre_comp.fps), '-pix_fmt', 'yuv420p', '-r', str(_pre_comp.fps),
'-af', 'apad', '-shortest', '-af', 'apad', '-shortest',
'-c:v', 'mpeg2video', '-g', '12', '-bf', '2', '-c:v', 'rawvideo',
'-b:v', '{}k'.format(_pre_comp.v_bitrate), '-c:a', 'pcm_s16le',
'-minrate', '{}k'.format(_pre_comp.v_bitrate),
'-maxrate', '{}k'.format(_pre_comp.v_bitrate),
'-bufsize', '{}k'.format(_pre_comp.v_bufsize),
'-c:a', 'mp2', '-b:a', '{}k'.format(_pre_comp.a_bitrate),
'-ar', str(_pre_comp.a_sample), '-ac', '2', '-ar', str(_pre_comp.a_sample), '-ac', '2',
'-threads', '2', '-f', 'mpegts', '-' '-threads', '2', '-f', 'avi', '-'
], ],
stdout=PIPE, stdout=PIPE,
bufsize=0 bufsize=0
@ -519,8 +530,7 @@ def main():
playout = Popen( playout = Popen(
[ [
'ffmpeg', '-v', 'info', '-hide_banner', '-nostats', '-re', 'ffmpeg', '-v', 'info', '-hide_banner', '-nostats', '-re',
'-fflags', '+igndts', '-thread_queue_size', '512', '-thread_queue_size', '256', '-i', 'pipe:0'
'-i', 'pipe:0', '-fflags', '+genpts'
] + ] +
list(_playout.logo) + list(_playout.logo) +
list(_playout.filter) + list(_playout.filter) +