change variable names, to try to follow style conventions
This commit is contained in:
parent
6abadf9543
commit
0a1467b093
@ -21,7 +21,7 @@
|
|||||||
import os
|
import os
|
||||||
from pydoc import locate
|
from pydoc import locate
|
||||||
|
|
||||||
from ffplayout.utils import PLAYOUT, STDIN_ARGS, validate_ffmpeg_libs
|
from ffplayout.utils import playout, stdin_args, validate_ffmpeg_libs
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if os.name != 'posix':
|
if os.name != 'posix':
|
||||||
@ -40,8 +40,8 @@ def main():
|
|||||||
play out depending on output mode
|
play out depending on output mode
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if STDIN_ARGS.mode:
|
if stdin_args.mode:
|
||||||
output = locate(f'ffplayout.output.{STDIN_ARGS.mode}.output')
|
output = locate(f'ffplayout.output.{stdin_args.mode}.output')
|
||||||
output()
|
output()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -53,7 +53,7 @@ def main():
|
|||||||
and output != '__init__.py':
|
and output != '__init__.py':
|
||||||
mode = os.path.splitext(output)[0]
|
mode = os.path.splitext(output)[0]
|
||||||
|
|
||||||
if mode == PLAYOUT.mode:
|
if mode == playout.mode:
|
||||||
output = locate(f'ffplayout.output.{mode}.output')
|
output = locate(f'ffplayout.output.{mode}.output')
|
||||||
output()
|
output()
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from ffplayout.utils import STDIN_ARGS, get_float
|
from ffplayout.utils import get_float, stdin_args
|
||||||
|
|
||||||
|
|
||||||
def filter_link(node):
|
def filter_link(node):
|
||||||
@ -6,5 +6,5 @@ def filter_link(node):
|
|||||||
set audio volume
|
set audio volume
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if STDIN_ARGS.volume and get_float(STDIN_ARGS.volume, False):
|
if stdin_args.volume and get_float(stdin_args.volume, False):
|
||||||
return f'volume={STDIN_ARGS.volume}'
|
return f'volume={stdin_args.volume}'
|
||||||
|
@ -23,7 +23,8 @@ import re
|
|||||||
from glob import glob
|
from glob import glob
|
||||||
from pydoc import locate
|
from pydoc import locate
|
||||||
|
|
||||||
from ffplayout.utils import GENERAL, PRE, TEXT, is_advertisement, messenger
|
from ffplayout.utils import (is_advertisement, lower_third, messenger, pre,
|
||||||
|
sync_op)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# building filters,
|
# building filters,
|
||||||
@ -35,12 +36,12 @@ def text_filter():
|
|||||||
filter_chain = []
|
filter_chain = []
|
||||||
font = ''
|
font = ''
|
||||||
|
|
||||||
if TEXT.add_text and TEXT.over_pre:
|
if lower_third.add_text and lower_third.over_pre:
|
||||||
if TEXT.fontfile and os.path.isfile(TEXT.fontfile):
|
if lower_third.fontfile and os.path.isfile(lower_third.fontfile):
|
||||||
font = f":fontfile='{TEXT.fontfile}'"
|
font = f":fontfile='{lower_third.fontfile}'"
|
||||||
filter_chain = [
|
filter_chain = [
|
||||||
"null,zmq=b=tcp\\\\://'{}',drawtext=text=''{}".format(
|
"null,zmq=b=tcp\\\\://'{}',drawtext=text=''{}".format(
|
||||||
TEXT.address.replace(':', '\\:'), font)]
|
lower_third.address.replace(':', '\\:'), font)]
|
||||||
|
|
||||||
return filter_chain
|
return filter_chain
|
||||||
|
|
||||||
@ -67,13 +68,13 @@ def pad_filter(probe):
|
|||||||
filter_chain = []
|
filter_chain = []
|
||||||
|
|
||||||
if not math.isclose(probe.video[0]['aspect'],
|
if not math.isclose(probe.video[0]['aspect'],
|
||||||
PRE.aspect, abs_tol=0.03):
|
pre.aspect, abs_tol=0.03):
|
||||||
if probe.video[0]['aspect'] < PRE.aspect:
|
if probe.video[0]['aspect'] < pre.aspect:
|
||||||
filter_chain.append(
|
filter_chain.append(
|
||||||
f'pad=ih*{PRE.w}/{PRE.h}/sar:ih:(ow-iw)/2:(oh-ih)/2')
|
f'pad=ih*{pre.w}/{pre.h}/sar:ih:(ow-iw)/2:(oh-ih)/2')
|
||||||
elif probe.video[0]['aspect'] > PRE.aspect:
|
elif probe.video[0]['aspect'] > pre.aspect:
|
||||||
filter_chain.append(
|
filter_chain.append(
|
||||||
f'pad=iw:iw*{PRE.h}/{PRE.w}/sar:(ow-iw)/2:(oh-ih)/2')
|
f'pad=iw:iw*{pre.h}/{pre.w}/sar:(ow-iw)/2:(oh-ih)/2')
|
||||||
|
|
||||||
return filter_chain
|
return filter_chain
|
||||||
|
|
||||||
@ -84,8 +85,8 @@ def fps_filter(probe):
|
|||||||
"""
|
"""
|
||||||
filter_chain = []
|
filter_chain = []
|
||||||
|
|
||||||
if probe.video[0]['fps'] != PRE.fps:
|
if probe.video[0]['fps'] != pre.fps:
|
||||||
filter_chain.append(f'fps={PRE.fps}')
|
filter_chain.append(f'fps={pre.fps}')
|
||||||
|
|
||||||
return filter_chain
|
return filter_chain
|
||||||
|
|
||||||
@ -97,13 +98,13 @@ def scale_filter(probe):
|
|||||||
"""
|
"""
|
||||||
filter_chain = []
|
filter_chain = []
|
||||||
|
|
||||||
if int(probe.video[0]['width']) != PRE.w or \
|
if int(probe.video[0]['width']) != pre.w or \
|
||||||
int(probe.video[0]['height']) != PRE.h:
|
int(probe.video[0]['height']) != pre.h:
|
||||||
filter_chain.append(f'scale={PRE.w}:{PRE.h}')
|
filter_chain.append(f'scale={pre.w}:{pre.h}')
|
||||||
|
|
||||||
if not math.isclose(probe.video[0]['aspect'],
|
if not math.isclose(probe.video[0]['aspect'],
|
||||||
PRE.aspect, abs_tol=0.03):
|
pre.aspect, abs_tol=0.03):
|
||||||
filter_chain.append(f'setdar=dar={PRE.aspect}')
|
filter_chain.append(f'setdar=dar={pre.aspect}')
|
||||||
|
|
||||||
return filter_chain
|
return filter_chain
|
||||||
|
|
||||||
@ -132,22 +133,22 @@ def overlay_filter(duration, ad, ad_last, ad_next):
|
|||||||
logo_filter = '[v]null'
|
logo_filter = '[v]null'
|
||||||
scale_filter = ''
|
scale_filter = ''
|
||||||
|
|
||||||
if PRE.add_logo and os.path.isfile(PRE.logo) and not ad:
|
if pre.add_logo and os.path.isfile(pre.logo) and not ad:
|
||||||
logo_chain = []
|
logo_chain = []
|
||||||
if PRE.logo_scale and \
|
if pre.logo_scale and \
|
||||||
re.match(r'\d+:-?\d+', PRE.logo_scale):
|
re.match(r'\d+:-?\d+', pre.logo_scale):
|
||||||
scale_filter = f'scale={PRE.logo_scale},'
|
scale_filter = f'scale={pre.logo_scale},'
|
||||||
logo_extras = (f'format=rgba,{scale_filter}'
|
logo_extras = (f'format=rgba,{scale_filter}'
|
||||||
f'colorchannelmixer=aa={PRE.logo_opacity}')
|
f'colorchannelmixer=aa={pre.logo_opacity}')
|
||||||
loop = 'loop=loop=-1:size=1:start=0'
|
loop = 'loop=loop=-1:size=1:start=0'
|
||||||
logo_chain.append(f'movie={PRE.logo},{loop},{logo_extras}')
|
logo_chain.append(f'movie={pre.logo},{loop},{logo_extras}')
|
||||||
if ad_last:
|
if ad_last:
|
||||||
logo_chain.append('fade=in:st=0:d=1.0:alpha=1')
|
logo_chain.append('fade=in:st=0:d=1.0:alpha=1')
|
||||||
if ad_next:
|
if ad_next:
|
||||||
logo_chain.append(f'fade=out:st={duration - 1}:d=1.0:alpha=1')
|
logo_chain.append(f'fade=out:st={duration - 1}:d=1.0:alpha=1')
|
||||||
|
|
||||||
logo_filter = (f'{",".join(logo_chain)}[l];[v][l]'
|
logo_filter = (f'{",".join(logo_chain)}[l];[v][l]'
|
||||||
f'{PRE.logo_filter}:shortest=1')
|
f'{pre.logo_filter}:shortest=1')
|
||||||
|
|
||||||
return logo_filter
|
return logo_filter
|
||||||
|
|
||||||
@ -172,9 +173,9 @@ def add_loudnorm(probe):
|
|||||||
"""
|
"""
|
||||||
loud_filter = []
|
loud_filter = []
|
||||||
|
|
||||||
if probe.audio and PRE.add_loudnorm:
|
if probe.audio and pre.add_loudnorm:
|
||||||
loud_filter = [
|
loud_filter = [
|
||||||
f'loudnorm=I={PRE.loud_i}:TP={PRE.loud_tp}:LRA={PRE.loud_lra}']
|
f'loudnorm=I={pre.loud_i}:TP={pre.loud_tp}:LRA={pre.loud_lra}']
|
||||||
|
|
||||||
return loud_filter
|
return loud_filter
|
||||||
|
|
||||||
@ -209,11 +210,11 @@ def extend_video(probe, duration, target_duration):
|
|||||||
def realtime_filter(duration, track=''):
|
def realtime_filter(duration, track=''):
|
||||||
speed_filter = ''
|
speed_filter = ''
|
||||||
|
|
||||||
if PRE.realtime:
|
if pre.realtime:
|
||||||
speed_filter = f',{track}realtime=speed=1'
|
speed_filter = f',{track}realtime=speed=1'
|
||||||
|
|
||||||
if GENERAL.time_delta < 0:
|
if sync_op.time_delta < 0:
|
||||||
speed = duration / (duration + GENERAL.time_delta)
|
speed = duration / (duration + sync_op.time_delta)
|
||||||
|
|
||||||
if speed < 1.1:
|
if speed < 1.1:
|
||||||
speed_filter = f',{track}realtime=speed={speed}'
|
speed_filter = f',{track}realtime=speed={speed}'
|
||||||
@ -229,11 +230,11 @@ def split_filter(filter_type):
|
|||||||
if filter_type == 'a':
|
if filter_type == 'a':
|
||||||
prefix = 'a'
|
prefix = 'a'
|
||||||
|
|
||||||
if PRE.output_count > 1:
|
if pre.output_count > 1:
|
||||||
for num in range(PRE.output_count):
|
for num in range(pre.output_count):
|
||||||
map_node.append(f'[{filter_type}out{num + 1}]')
|
map_node.append(f'[{filter_type}out{num + 1}]')
|
||||||
|
|
||||||
_filter = f',{prefix}split={PRE.output_count}{"".join(map_node)}'
|
_filter = f',{prefix}split={pre.output_count}{"".join(map_node)}'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
_filter = f'[{filter_type}out1]'
|
_filter = f'[{filter_type}out1]'
|
||||||
@ -241,11 +242,11 @@ def split_filter(filter_type):
|
|||||||
return _filter
|
return _filter
|
||||||
|
|
||||||
|
|
||||||
def custom_filter(type, node):
|
def custom_filter(filter_type, node):
|
||||||
filter_dir = os.path.dirname(os.path.abspath(__file__))
|
filter_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
filters = []
|
filters = []
|
||||||
|
|
||||||
for filter_file in glob(os.path.join(filter_dir, f'{type}_*')):
|
for filter_file in glob(os.path.join(filter_dir, f'{filter_type}_*')):
|
||||||
filter_ = os.path.splitext(os.path.basename(filter_file))[0]
|
filter_ = os.path.splitext(os.path.basename(filter_file))[0]
|
||||||
filter_function = locate(f'ffplayout.filters.{filter_}.filter_link')
|
filter_function = locate(f'ffplayout.filters.{filter_}.filter_link')
|
||||||
link = filter_function(node)
|
link = filter_function(node)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ffplayout.utils import TEXT
|
from ffplayout.utils import lower_third
|
||||||
|
|
||||||
|
|
||||||
def filter_link(node):
|
def filter_link(node):
|
||||||
@ -10,12 +10,12 @@ def filter_link(node):
|
|||||||
"""
|
"""
|
||||||
font = ''
|
font = ''
|
||||||
source = os.path.basename(node.get('source'))
|
source = os.path.basename(node.get('source'))
|
||||||
match = re.match(TEXT.regex, source)
|
match = re.match(lower_third.regex, source)
|
||||||
title = match[1] if match else source
|
title = match[1] if match else source
|
||||||
|
|
||||||
if TEXT.fontfile and os.path.isfile(TEXT.fontfile):
|
if lower_third.fontfile and os.path.isfile(lower_third.fontfile):
|
||||||
font = f":fontfile='{TEXT.fontfile}'"
|
font = f":fontfile='{lower_third.fontfile}'"
|
||||||
|
|
||||||
if TEXT.text_from_filename:
|
if lower_third.text_from_filename:
|
||||||
escape = title.replace("'", "'\\\\\\''").replace("%", "\\\\\\%")
|
escape = title.replace("'", "'\\\\\\''").replace("%", "\\\\\\%")
|
||||||
return f"drawtext=text='{escape}':{TEXT.style}{font}"
|
return f"drawtext=text='{escape}':{lower_third.style}{font}"
|
||||||
|
@ -27,7 +27,7 @@ from watchdog.events import PatternMatchingEventHandler
|
|||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
from .filters.default import build_filtergraph
|
from .filters.default import build_filtergraph
|
||||||
from .utils import FF, STDIN_ARGS, STORAGE, MediaProbe, messenger
|
from .utils import MediaProbe, ff_proc, messenger, stdin_args, storage
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# folder watcher
|
# folder watcher
|
||||||
@ -43,21 +43,21 @@ class MediaStore:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.store = []
|
self.store = []
|
||||||
|
|
||||||
if STDIN_ARGS.folder:
|
if stdin_args.folder:
|
||||||
self.folder = STDIN_ARGS.folder
|
self.folder = stdin_args.folder
|
||||||
else:
|
else:
|
||||||
self.folder = STORAGE.path
|
self.folder = storage.path
|
||||||
|
|
||||||
self.fill()
|
self.fill()
|
||||||
|
|
||||||
def fill(self):
|
def fill(self):
|
||||||
for ext in STORAGE.extensions:
|
for ext in storage.extensions:
|
||||||
self.store.extend(
|
self.store.extend(
|
||||||
glob.glob(os.path.join(self.folder, '**', f'*{ext}'),
|
glob.glob(os.path.join(self.folder, '**', f'*{ext}'),
|
||||||
recursive=True))
|
recursive=True))
|
||||||
|
|
||||||
def sort_or_radomize(self):
|
def sort_or_radomize(self):
|
||||||
if STORAGE.shuffle:
|
if storage.shuffle:
|
||||||
self.rand()
|
self.rand()
|
||||||
else:
|
else:
|
||||||
self.sort()
|
self.sort()
|
||||||
@ -86,7 +86,7 @@ class MediaWatcher:
|
|||||||
|
|
||||||
def __init__(self, media):
|
def __init__(self, media):
|
||||||
self._media = media
|
self._media = media
|
||||||
self.extensions = [f'*{ext}' for ext in STORAGE.extensions]
|
self.extensions = [f'*{ext}' for ext in storage.extensions]
|
||||||
self.current_clip = None
|
self.current_clip = None
|
||||||
|
|
||||||
self.event_handler = PatternMatchingEventHandler(
|
self.event_handler = PatternMatchingEventHandler(
|
||||||
@ -120,7 +120,7 @@ class MediaWatcher:
|
|||||||
f'Move file from "{event.src_path}" to "{event.dest_path}"')
|
f'Move file from "{event.src_path}" to "{event.dest_path}"')
|
||||||
|
|
||||||
if self.current_clip == event.src_path:
|
if self.current_clip == event.src_path:
|
||||||
FF.decoder.terminate()
|
ff_proc.decoder.terminate()
|
||||||
|
|
||||||
def on_deleted(self, event):
|
def on_deleted(self, event):
|
||||||
self._media.remove(event.src_path)
|
self._media.remove(event.src_path)
|
||||||
@ -128,7 +128,7 @@ class MediaWatcher:
|
|||||||
messenger.info(f'Remove file from media list: "{event.src_path}"')
|
messenger.info(f'Remove file from media list: "{event.src_path}"')
|
||||||
|
|
||||||
if self.current_clip == event.src_path:
|
if self.current_clip == event.src_path:
|
||||||
FF.decoder.terminate()
|
ff_proc.decoder.terminate()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.observer.stop()
|
self.observer.stop()
|
||||||
|
@ -21,9 +21,9 @@ from threading import Thread
|
|||||||
|
|
||||||
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
|
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
|
||||||
from ffplayout.playlist import GetSourceFromPlaylist
|
from ffplayout.playlist import GetSourceFromPlaylist
|
||||||
from ffplayout.utils import (FF, LOG, PLAYLIST, PRE, STDIN_ARGS, TEXT,
|
from ffplayout.utils import (ff_proc, ffmpeg_stderr_reader, log, lower_third,
|
||||||
ffmpeg_stderr_reader, messenger, pre_audio_codec,
|
messenger, playlist, pre, pre_audio_codec,
|
||||||
terminate_processes)
|
stdin_args, terminate_processes)
|
||||||
|
|
||||||
_WINDOWS = os.name == 'nt'
|
_WINDOWS = os.name == 'nt'
|
||||||
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424
|
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424
|
||||||
@ -36,21 +36,22 @@ def output():
|
|||||||
overlay = []
|
overlay = []
|
||||||
|
|
||||||
ff_pre_settings = [
|
ff_pre_settings = [
|
||||||
'-pix_fmt', 'yuv420p', '-r', str(PRE.fps),
|
'-pix_fmt', 'yuv420p', '-r', str(pre.fps),
|
||||||
'-c:v', 'mpeg2video', '-intra',
|
'-c:v', 'mpeg2video', '-intra',
|
||||||
'-b:v', f'{PRE.v_bitrate}k',
|
'-b:v', f'{pre.v_bitrate}k',
|
||||||
'-minrate', f'{PRE.v_bitrate}k',
|
'-minrate', f'{pre.v_bitrate}k',
|
||||||
'-maxrate', f'{PRE.v_bitrate}k',
|
'-maxrate', f'{pre.v_bitrate}k',
|
||||||
'-bufsize', f'{PRE.v_bufsize}k'
|
'-bufsize', f'{pre.v_bufsize}k'
|
||||||
] + pre_audio_codec() + ['-f', 'mpegts', '-']
|
] + pre_audio_codec() + ['-f', 'mpegts', '-']
|
||||||
|
|
||||||
if TEXT.add_text and not TEXT.over_pre:
|
if lower_third.add_text and not lower_third.over_pre:
|
||||||
messenger.info(
|
messenger.info(
|
||||||
f'Using drawtext node, listening on address: {TEXT.address}')
|
f'Using drawtext node, listening on address: {lower_third.address}'
|
||||||
|
)
|
||||||
overlay = [
|
overlay = [
|
||||||
'-vf',
|
'-vf',
|
||||||
"null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format(
|
"null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format(
|
||||||
TEXT.address.replace(':', '\\:'), TEXT.fontfile)
|
lower_third.address.replace(':', '\\:'), lower_third.fontfile)
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -60,14 +61,14 @@ def output():
|
|||||||
|
|
||||||
messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"')
|
messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"')
|
||||||
|
|
||||||
FF.encoder = Popen(enc_cmd, stderr=PIPE, stdin=PIPE, stdout=None)
|
ff_proc.encoder = Popen(enc_cmd, stderr=PIPE, stdin=PIPE, stdout=None)
|
||||||
|
|
||||||
enc_err_thread = Thread(target=ffmpeg_stderr_reader,
|
enc_err_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(FF.encoder.stderr, False))
|
args=(ff_proc.encoder.stderr, False))
|
||||||
enc_err_thread.daemon = True
|
enc_err_thread.daemon = True
|
||||||
enc_err_thread.start()
|
enc_err_thread.start()
|
||||||
|
|
||||||
if PLAYLIST.mode and not STDIN_ARGS.folder:
|
if playlist.mode and not stdin_args.folder:
|
||||||
watcher = None
|
watcher = None
|
||||||
get_source = GetSourceFromPlaylist()
|
get_source = GetSourceFromPlaylist()
|
||||||
else:
|
else:
|
||||||
@ -86,23 +87,25 @@ def output():
|
|||||||
f'seconds: {node.get("source")}')
|
f'seconds: {node.get("source")}')
|
||||||
|
|
||||||
dec_cmd = [
|
dec_cmd = [
|
||||||
'ffmpeg', '-v', LOG.ff_level.lower(),
|
'ffmpeg', '-v', log.ff_level.lower(),
|
||||||
'-hide_banner', '-nostats'
|
'-hide_banner', '-nostats'
|
||||||
] + node['src_cmd'] + node['filter'] + ff_pre_settings
|
] + node['src_cmd'] + node['filter'] + ff_pre_settings
|
||||||
|
|
||||||
messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"')
|
messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"')
|
||||||
|
|
||||||
with Popen(dec_cmd, stdout=PIPE, stderr=PIPE) as FF.decoder:
|
with Popen(
|
||||||
|
dec_cmd, stdout=PIPE, stderr=PIPE) as ff_proc.decoder:
|
||||||
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(FF.decoder.stderr, True))
|
args=(ff_proc.decoder.stderr,
|
||||||
|
True))
|
||||||
dec_err_thread.daemon = True
|
dec_err_thread.daemon = True
|
||||||
dec_err_thread.start()
|
dec_err_thread.start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
buf = FF.decoder.stdout.read(COPY_BUFSIZE)
|
buf = ff_proc.decoder.stdout.read(COPY_BUFSIZE)
|
||||||
if not buf:
|
if not buf:
|
||||||
break
|
break
|
||||||
FF.encoder.stdin.write(buf)
|
ff_proc.encoder.stdin.write(buf)
|
||||||
|
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
messenger.error('Broken Pipe!')
|
messenger.error('Broken Pipe!')
|
||||||
@ -117,10 +120,10 @@ def output():
|
|||||||
terminate_processes(watcher)
|
terminate_processes(watcher)
|
||||||
|
|
||||||
# close encoder when nothing is to do anymore
|
# close encoder when nothing is to do anymore
|
||||||
if FF.encoder.poll() is None:
|
if ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if FF.encoder.poll() is None:
|
if ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
FF.encoder.wait()
|
ff_proc.encoder.wait()
|
||||||
|
@ -23,8 +23,8 @@ from threading import Thread
|
|||||||
|
|
||||||
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
|
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
|
||||||
from ffplayout.playlist import GetSourceFromPlaylist
|
from ffplayout.playlist import GetSourceFromPlaylist
|
||||||
from ffplayout.utils import (FF, LOG, PLAYLIST, PLAYOUT, STDIN_ARGS,
|
from ffplayout.utils import (ff_proc, ffmpeg_stderr_reader, get_date, log,
|
||||||
ffmpeg_stderr_reader, get_date, messenger,
|
messenger, playlist, playout, stdin_args,
|
||||||
terminate_processes)
|
terminate_processes)
|
||||||
|
|
||||||
|
|
||||||
@ -35,15 +35,15 @@ def clean_ts():
|
|||||||
then it checks if files on harddrive are older then this first *.ts
|
then it checks if files on harddrive are older then this first *.ts
|
||||||
and if so delete them
|
and if so delete them
|
||||||
"""
|
"""
|
||||||
playlists = [p for p in PLAYOUT.hls_output if 'm3u8' in p]
|
m3u8_files = [p for p in playout.hls_output if 'm3u8' in p]
|
||||||
|
|
||||||
for playlist in playlists:
|
for m3u8_file in m3u8_files:
|
||||||
messenger.debug(f'cleanup *.ts files from: "{playlist}"')
|
messenger.debug(f'cleanup *.ts files from: "{m3u8_file}"')
|
||||||
test_num = 0
|
test_num = 0
|
||||||
hls_path = os.path.dirname(playlist)
|
hls_path = os.path.dirname(m3u8_file)
|
||||||
|
|
||||||
if os.path.isfile(playlist):
|
if os.path.isfile(m3u8_file):
|
||||||
with open(playlist, 'r') as m3u8:
|
with open(m3u8_file, 'r') as m3u8:
|
||||||
for line in m3u8:
|
for line in m3u8:
|
||||||
if '.ts' in line:
|
if '.ts' in line:
|
||||||
test_num = int(re.findall(r'(\d+).ts', line)[0])
|
test_num = int(re.findall(r'(\d+).ts', line)[0])
|
||||||
@ -66,7 +66,7 @@ def output():
|
|||||||
year = get_date(False).split('-')[0]
|
year = get_date(False).split('-')[0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if PLAYLIST.mode and not STDIN_ARGS.folder:
|
if playlist.mode and not stdin_args.folder:
|
||||||
watcher = None
|
watcher = None
|
||||||
get_source = GetSourceFromPlaylist()
|
get_source = GetSourceFromPlaylist()
|
||||||
else:
|
else:
|
||||||
@ -83,20 +83,21 @@ def output():
|
|||||||
messenger.info(f'Play: {node.get("source")}')
|
messenger.info(f'Play: {node.get("source")}')
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
'ffmpeg', '-v', LOG.ff_level.lower(), '-hide_banner',
|
'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner',
|
||||||
'-nostats'
|
'-nostats'
|
||||||
] + node['src_cmd'] + node['filter'] + [
|
] + node['src_cmd'] + node['filter'] + [
|
||||||
'-metadata', 'service_name=' + PLAYOUT.name,
|
'-metadata', 'service_name=' + playout.name,
|
||||||
'-metadata', 'service_provider=' + PLAYOUT.provider,
|
'-metadata', 'service_provider=' + playout.provider,
|
||||||
'-metadata', 'year={}'.format(year)
|
'-metadata', 'year={}'.format(year)
|
||||||
] + PLAYOUT.ffmpeg_param + PLAYOUT.hls_output
|
] + playout.ffmpeg_param + playout.hls_output
|
||||||
|
|
||||||
messenger.debug(f'Encoder CMD: "{" ".join(cmd)}"')
|
messenger.debug(f'Encoder CMD: "{" ".join(cmd)}"')
|
||||||
|
|
||||||
FF.encoder = Popen(cmd, stdin=PIPE, stderr=PIPE)
|
ff_proc.encoder = Popen(cmd, stdin=PIPE, stderr=PIPE)
|
||||||
|
|
||||||
stderr_reader_thread = Thread(target=ffmpeg_stderr_reader,
|
stderr_reader_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(FF.encoder.stderr, False))
|
args=(ff_proc.encoder.stderr,
|
||||||
|
False))
|
||||||
stderr_reader_thread.daemon = True
|
stderr_reader_thread.daemon = True
|
||||||
stderr_reader_thread.start()
|
stderr_reader_thread.start()
|
||||||
stderr_reader_thread.join()
|
stderr_reader_thread.join()
|
||||||
@ -118,10 +119,10 @@ def output():
|
|||||||
terminate_processes(watcher)
|
terminate_processes(watcher)
|
||||||
|
|
||||||
# close encoder when nothing is to do anymore
|
# close encoder when nothing is to do anymore
|
||||||
if FF.encoder.poll() is None:
|
if ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if FF.encoder.poll() is None:
|
if ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
FF.encoder.wait()
|
ff_proc.encoder.wait()
|
||||||
|
@ -21,9 +21,9 @@ from threading import Thread
|
|||||||
|
|
||||||
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
|
from ffplayout.folder import GetSourceFromFolder, MediaStore, MediaWatcher
|
||||||
from ffplayout.playlist import GetSourceFromPlaylist
|
from ffplayout.playlist import GetSourceFromPlaylist
|
||||||
from ffplayout.utils import (FF, LOG, PLAYLIST, PLAYOUT, PRE, STDIN_ARGS, TEXT,
|
from ffplayout.utils import (ff_proc, ffmpeg_stderr_reader, get_date, log,
|
||||||
ffmpeg_stderr_reader, get_date, messenger,
|
lower_third, messenger, playlist, playout, pre,
|
||||||
pre_audio_codec, terminate_processes)
|
pre_audio_codec, stdin_args, terminate_processes)
|
||||||
|
|
||||||
_WINDOWS = os.name == 'nt'
|
_WINDOWS = os.name == 'nt'
|
||||||
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424
|
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 65424
|
||||||
@ -38,43 +38,44 @@ def output():
|
|||||||
overlay = []
|
overlay = []
|
||||||
|
|
||||||
ff_pre_settings = [
|
ff_pre_settings = [
|
||||||
'-pix_fmt', 'yuv420p', '-r', str(PRE.fps),
|
'-pix_fmt', 'yuv420p', '-r', str(pre.fps),
|
||||||
'-c:v', 'mpeg2video', '-intra',
|
'-c:v', 'mpeg2video', '-intra',
|
||||||
'-b:v', f'{PRE.v_bitrate}k',
|
'-b:v', f'{pre.v_bitrate}k',
|
||||||
'-minrate', f'{PRE.v_bitrate}k',
|
'-minrate', f'{pre.v_bitrate}k',
|
||||||
'-maxrate', f'{PRE.v_bitrate}k',
|
'-maxrate', f'{pre.v_bitrate}k',
|
||||||
'-bufsize', f'{PRE.v_bufsize}k'
|
'-bufsize', f'{pre.v_bufsize}k'
|
||||||
] + pre_audio_codec() + ['-f', 'mpegts', '-']
|
] + pre_audio_codec() + ['-f', 'mpegts', '-']
|
||||||
|
|
||||||
if TEXT.add_text and not TEXT.over_pre:
|
if lower_third.add_text and not lower_third.over_pre:
|
||||||
messenger.info(
|
messenger.info(
|
||||||
f'Using drawtext node, listening on address: {TEXT.address}')
|
f'Using drawtext node, listening on address: {lower_third.address}'
|
||||||
|
)
|
||||||
overlay = [
|
overlay = [
|
||||||
'-vf',
|
'-vf',
|
||||||
"null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format(
|
"null,zmq=b=tcp\\\\://'{}',drawtext=text='':fontfile='{}'".format(
|
||||||
TEXT.address.replace(':', '\\:'), TEXT.fontfile)
|
lower_third.address.replace(':', '\\:'), lower_third.fontfile)
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
enc_cmd = [
|
enc_cmd = [
|
||||||
'ffmpeg', '-v', LOG.ff_level.lower(), '-hide_banner',
|
'ffmpeg', '-v', log.ff_level.lower(), '-hide_banner',
|
||||||
'-nostats', '-re', '-thread_queue_size', '160', '-i', 'pipe:0'
|
'-nostats', '-re', '-thread_queue_size', '160', '-i', 'pipe:0'
|
||||||
] + overlay + [
|
] + overlay + [
|
||||||
'-metadata', 'service_name=' + PLAYOUT.name,
|
'-metadata', 'service_name=' + playout.name,
|
||||||
'-metadata', 'service_provider=' + PLAYOUT.provider,
|
'-metadata', 'service_provider=' + playout.provider,
|
||||||
'-metadata', f'year={year}'
|
'-metadata', f'year={year}'
|
||||||
] + PLAYOUT.ffmpeg_param + PLAYOUT.stream_output
|
] + playout.ffmpeg_param + playout.stream_output
|
||||||
|
|
||||||
messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"')
|
messenger.debug(f'Encoder CMD: "{" ".join(enc_cmd)}"')
|
||||||
|
|
||||||
FF.encoder = Popen(enc_cmd, stdin=PIPE, stderr=PIPE)
|
ff_proc.encoder = Popen(enc_cmd, stdin=PIPE, stderr=PIPE)
|
||||||
|
|
||||||
enc_err_thread = Thread(target=ffmpeg_stderr_reader,
|
enc_err_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(FF.encoder.stderr, False))
|
args=(ff_proc.encoder.stderr, False))
|
||||||
enc_err_thread.daemon = True
|
enc_err_thread.daemon = True
|
||||||
enc_err_thread.start()
|
enc_err_thread.start()
|
||||||
|
|
||||||
if PLAYLIST.mode and not STDIN_ARGS.folder:
|
if playlist.mode and not stdin_args.folder:
|
||||||
watcher = None
|
watcher = None
|
||||||
get_source = GetSourceFromPlaylist()
|
get_source = GetSourceFromPlaylist()
|
||||||
else:
|
else:
|
||||||
@ -91,23 +92,25 @@ def output():
|
|||||||
messenger.info(f'Play: {node.get("source")}')
|
messenger.info(f'Play: {node.get("source")}')
|
||||||
|
|
||||||
dec_cmd = [
|
dec_cmd = [
|
||||||
'ffmpeg', '-v', LOG.ff_level.lower(),
|
'ffmpeg', '-v', log.ff_level.lower(),
|
||||||
'-hide_banner', '-nostats'
|
'-hide_banner', '-nostats'
|
||||||
] + node['src_cmd'] + node['filter'] + ff_pre_settings
|
] + node['src_cmd'] + node['filter'] + ff_pre_settings
|
||||||
|
|
||||||
messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"')
|
messenger.debug(f'Decoder CMD: "{" ".join(dec_cmd)}"')
|
||||||
|
|
||||||
with Popen(dec_cmd, stdout=PIPE, stderr=PIPE) as FF.decoder:
|
with Popen(
|
||||||
|
dec_cmd, stdout=PIPE, stderr=PIPE) as ff_proc.decoder:
|
||||||
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
dec_err_thread = Thread(target=ffmpeg_stderr_reader,
|
||||||
args=(FF.decoder.stderr, True))
|
args=(ff_proc.decoder.stderr,
|
||||||
|
True))
|
||||||
dec_err_thread.daemon = True
|
dec_err_thread.daemon = True
|
||||||
dec_err_thread.start()
|
dec_err_thread.start()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
buf = FF.decoder.stdout.read(COPY_BUFSIZE)
|
buf = ff_proc.decoder.stdout.read(COPY_BUFSIZE)
|
||||||
if not buf:
|
if not buf:
|
||||||
break
|
break
|
||||||
FF.encoder.stdin.write(buf)
|
ff_proc.encoder.stdin.write(buf)
|
||||||
|
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
messenger.error('Broken Pipe!')
|
messenger.error('Broken Pipe!')
|
||||||
@ -122,10 +125,10 @@ def output():
|
|||||||
terminate_processes(watcher)
|
terminate_processes(watcher)
|
||||||
|
|
||||||
# close encoder when nothing is to do anymore
|
# close encoder when nothing is to do anymore
|
||||||
if FF.encoder.poll() is None:
|
if ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
if FF.encoder.poll() is None:
|
if ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
FF.encoder.wait()
|
ff_proc.encoder.wait()
|
||||||
|
@ -28,9 +28,9 @@ from threading import Thread
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from .filters.default import build_filtergraph
|
from .filters.default import build_filtergraph
|
||||||
from .utils import (GENERAL, PLAYLIST, STDIN_ARGS, MediaProbe, check_sync,
|
from .utils import (MediaProbe, check_sync, get_date, get_delta, get_float,
|
||||||
get_date, get_delta, get_float, get_time, messenger,
|
get_time, messenger, playlist, src_or_dummy, stdin_args,
|
||||||
src_or_dummy, valid_json)
|
sync_op, valid_json)
|
||||||
|
|
||||||
|
|
||||||
def handle_list_init(node):
|
def handle_list_init(node):
|
||||||
@ -103,13 +103,13 @@ def timed_source(node, last):
|
|||||||
delta, total_delta = get_delta(node['begin'])
|
delta, total_delta = get_delta(node['begin'])
|
||||||
node_ = None
|
node_ = None
|
||||||
|
|
||||||
if not STDIN_ARGS.loop and PLAYLIST.length:
|
if not stdin_args.loop and playlist.length:
|
||||||
messenger.debug(f'delta: {delta:f}')
|
messenger.debug(f'delta: {delta:f}')
|
||||||
messenger.debug(f'total_delta: {total_delta:f}')
|
messenger.debug(f'total_delta: {total_delta:f}')
|
||||||
check_sync(delta)
|
check_sync(delta)
|
||||||
|
|
||||||
if (total_delta > node['out'] - node['seek'] and not last) \
|
if (total_delta > node['out'] - node['seek'] and not last) \
|
||||||
or STDIN_ARGS.loop or not PLAYLIST.length:
|
or stdin_args.loop or not playlist.length:
|
||||||
# when we are in the 24 houre range, get the clip
|
# when we are in the 24 houre range, get the clip
|
||||||
node_ = src_or_dummy(node)
|
node_ = src_or_dummy(node)
|
||||||
|
|
||||||
@ -126,12 +126,12 @@ def check_length(total_play_time, list_date):
|
|||||||
"""
|
"""
|
||||||
check if playlist is long enough
|
check if playlist is long enough
|
||||||
"""
|
"""
|
||||||
if PLAYLIST.length and total_play_time < PLAYLIST.length - 5 \
|
if playlist.length and total_play_time < playlist.length - 5 \
|
||||||
and not STDIN_ARGS.loop:
|
and not stdin_args.loop:
|
||||||
messenger.error(
|
messenger.error(
|
||||||
f'Playlist from {list_date} is not long enough!\n'
|
f'Playlist from {list_date} is not long enough!\n'
|
||||||
f'Total play time is: {timedelta(seconds=total_play_time)}, '
|
f'Total play time is: {timedelta(seconds=total_play_time)}, '
|
||||||
f'target length is: {timedelta(seconds=PLAYLIST.length)}'
|
f'target length is: {timedelta(seconds=playlist.length)}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -203,11 +203,11 @@ class PlaylistReader:
|
|||||||
self.nodes = {'program': []}
|
self.nodes = {'program': []}
|
||||||
self.error = False
|
self.error = False
|
||||||
|
|
||||||
if STDIN_ARGS.playlist:
|
if stdin_args.playlist:
|
||||||
json_file = STDIN_ARGS.playlist
|
json_file = stdin_args.playlist
|
||||||
else:
|
else:
|
||||||
year, month, day = self.list_date.split('-')
|
year, month, day = self.list_date.split('-')
|
||||||
json_file = os.path.join(PLAYLIST.path, year, month,
|
json_file = os.path.join(playlist.path, year, month,
|
||||||
f'{self.list_date}.json')
|
f'{self.list_date}.json')
|
||||||
|
|
||||||
if '://' in json_file:
|
if '://' in json_file:
|
||||||
@ -253,7 +253,7 @@ class GetSourceFromPlaylist:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.prev_date = get_date(True)
|
self.prev_date = get_date(True)
|
||||||
self.list_start = PLAYLIST.start
|
self.list_start = playlist.start
|
||||||
self.first = True
|
self.first = True
|
||||||
self.last = False
|
self.last = False
|
||||||
self.clip_nodes = []
|
self.clip_nodes = []
|
||||||
@ -261,7 +261,7 @@ class GetSourceFromPlaylist:
|
|||||||
self.node = None
|
self.node = None
|
||||||
self.prev_node = None
|
self.prev_node = None
|
||||||
self.next_node = None
|
self.next_node = None
|
||||||
self.playlist = PlaylistReader(get_date(True), 0.0)
|
self.playlist_reader = PlaylistReader(get_date(True), 0.0)
|
||||||
self.last_error = False
|
self.last_error = False
|
||||||
|
|
||||||
def get_playlist(self):
|
def get_playlist(self):
|
||||||
@ -269,26 +269,26 @@ class GetSourceFromPlaylist:
|
|||||||
read playlist from given date and fill clip_nodes
|
read playlist from given date and fill clip_nodes
|
||||||
when playlist is not available, reset relevant values
|
when playlist is not available, reset relevant values
|
||||||
"""
|
"""
|
||||||
self.playlist.read()
|
self.playlist_reader.read()
|
||||||
|
|
||||||
if self.last_error and not self.playlist.error and \
|
if self.last_error and not self.playlist_reader.error and \
|
||||||
self.playlist.list_date == self.prev_date:
|
self.playlist_reader.list_date == self.prev_date:
|
||||||
# when last playlist where not exists but now is there and
|
# when last playlist where not exists but now is there and
|
||||||
# is still the same playlist date,
|
# is still the same playlist date,
|
||||||
# set self.first to true to seek in clip
|
# set self.first to true to seek in clip
|
||||||
# only in this situation seek in is correct!!
|
# only in this situation seek in is correct!!
|
||||||
self.first = True
|
self.first = True
|
||||||
self.last_error = self.playlist.error
|
self.last_error = self.playlist_reader.error
|
||||||
|
|
||||||
if self.playlist.nodes.get('program'):
|
if self.playlist_reader.nodes.get('program'):
|
||||||
self.clip_nodes = self.playlist.nodes.get('program')
|
self.clip_nodes = self.playlist_reader.nodes.get('program')
|
||||||
self.node_count = len(self.clip_nodes)
|
self.node_count = len(self.clip_nodes)
|
||||||
|
|
||||||
if self.playlist.error:
|
if self.playlist_reader.error:
|
||||||
self.clip_nodes = []
|
self.clip_nodes = []
|
||||||
self.node_count = 0
|
self.node_count = 0
|
||||||
self.playlist.last_mod_time = 0.0
|
self.playlist_reader.last_mod_time = 0.0
|
||||||
self.last_error = self.playlist.error
|
self.last_error = self.playlist_reader.error
|
||||||
|
|
||||||
def init_time(self):
|
def init_time(self):
|
||||||
"""
|
"""
|
||||||
@ -296,12 +296,12 @@ class GetSourceFromPlaylist:
|
|||||||
"""
|
"""
|
||||||
self.last_time = get_time('full_sec')
|
self.last_time = get_time('full_sec')
|
||||||
|
|
||||||
if PLAYLIST.length:
|
if playlist.length:
|
||||||
total_playtime = PLAYLIST.length
|
total_playtime = playlist.length
|
||||||
else:
|
else:
|
||||||
total_playtime = 86400.0
|
total_playtime = 86400.0
|
||||||
|
|
||||||
if self.last_time < PLAYLIST.start:
|
if self.last_time < playlist.start:
|
||||||
self.last_time += total_playtime
|
self.last_time += total_playtime
|
||||||
|
|
||||||
def check_for_next_playlist(self, begin):
|
def check_for_next_playlist(self, begin):
|
||||||
@ -321,17 +321,17 @@ class GetSourceFromPlaylist:
|
|||||||
delta, total_delta = get_delta(begin)
|
delta, total_delta = get_delta(begin)
|
||||||
delta += seek + 1
|
delta += seek + 1
|
||||||
|
|
||||||
next_start = begin - PLAYLIST.start + out + delta
|
next_start = begin - playlist.start + out + delta
|
||||||
|
|
||||||
else:
|
else:
|
||||||
delta, total_delta = get_delta(begin)
|
delta, total_delta = get_delta(begin)
|
||||||
next_start = begin - PLAYLIST.start + GENERAL.threshold + delta
|
next_start = begin - playlist.start + sync_op.threshold + delta
|
||||||
|
|
||||||
if PLAYLIST.length and next_start >= PLAYLIST.length:
|
if playlist.length and next_start >= playlist.length:
|
||||||
self.prev_date = get_date(False, next_start)
|
self.prev_date = get_date(False, next_start)
|
||||||
self.playlist.list_date = self.prev_date
|
self.playlist_reader.list_date = self.prev_date
|
||||||
self.playlist.last_mod_time = 0.0
|
self.playlist_reader.last_mod_time = 0.0
|
||||||
self.last_time = PLAYLIST.start - 1
|
self.last_time = playlist.start - 1
|
||||||
self.clip_nodes = []
|
self.clip_nodes = []
|
||||||
|
|
||||||
def previous_and_next_node(self, index):
|
def previous_and_next_node(self, index):
|
||||||
@ -361,9 +361,9 @@ class GetSourceFromPlaylist:
|
|||||||
"""
|
"""
|
||||||
current_time = get_time('full_sec') - 86400
|
current_time = get_time('full_sec') - 86400
|
||||||
# balance small difference to start time
|
# balance small difference to start time
|
||||||
if PLAYLIST.start is not None and isclose(PLAYLIST.start,
|
if playlist.start is not None and isclose(playlist.start,
|
||||||
current_time, abs_tol=2):
|
current_time, abs_tol=2):
|
||||||
begin = PLAYLIST.start
|
begin = playlist.start
|
||||||
else:
|
else:
|
||||||
self.init_time()
|
self.init_time()
|
||||||
begin = self.last_time
|
begin = self.last_time
|
||||||
@ -385,14 +385,14 @@ class GetSourceFromPlaylist:
|
|||||||
"""
|
"""
|
||||||
handle except playlist end
|
handle except playlist end
|
||||||
"""
|
"""
|
||||||
if STDIN_ARGS.loop and self.node:
|
if stdin_args.loop and self.node:
|
||||||
# when loop paramter is set and playlist node exists,
|
# when loop paramter is set and playlist node exists,
|
||||||
# jump to playlist start and play again
|
# jump to playlist start and play again
|
||||||
self.list_start = self.last_time + 1
|
self.list_start = self.last_time + 1
|
||||||
self.node = None
|
self.node = None
|
||||||
messenger.info('Loop playlist')
|
messenger.info('Loop playlist')
|
||||||
|
|
||||||
elif begin == PLAYLIST.start or not self.clip_nodes:
|
elif begin == playlist.start or not self.clip_nodes:
|
||||||
# playlist not exist or is corrupt/empty
|
# playlist not exist or is corrupt/empty
|
||||||
messenger.error('Clip nodes are empty!')
|
messenger.error('Clip nodes are empty!')
|
||||||
self.first = False
|
self.first = False
|
||||||
@ -454,7 +454,7 @@ class GetSourceFromPlaylist:
|
|||||||
|
|
||||||
begin += self.node['out'] - self.node['seek']
|
begin += self.node['out'] - self.node['seek']
|
||||||
else:
|
else:
|
||||||
if not PLAYLIST.length and not STDIN_ARGS.loop:
|
if not playlist.length and not stdin_args.loop:
|
||||||
# when we reach playlist end, stop script
|
# when we reach playlist end, stop script
|
||||||
messenger.info('Playlist reached end!')
|
messenger.info('Playlist reached end!')
|
||||||
return None
|
return None
|
||||||
|
@ -98,7 +98,7 @@ for arg_file in glob(os.path.join(CONFIG_PATH, 'argparse_*')):
|
|||||||
**config
|
**config
|
||||||
)
|
)
|
||||||
|
|
||||||
STDIN_ARGS = stdin_parser.parse_args()
|
stdin_args = stdin_parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@ -127,17 +127,17 @@ def get_time(time_format):
|
|||||||
# default variables and values
|
# default variables and values
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
GENERAL = SimpleNamespace(time_delta=0)
|
sync_op = SimpleNamespace(time_delta=0)
|
||||||
MAIL = SimpleNamespace()
|
mail = SimpleNamespace()
|
||||||
LOG = SimpleNamespace()
|
log = SimpleNamespace()
|
||||||
PRE = SimpleNamespace()
|
pre = SimpleNamespace()
|
||||||
PLAYLIST = SimpleNamespace()
|
playlist = SimpleNamespace()
|
||||||
STORAGE = SimpleNamespace()
|
storage = SimpleNamespace()
|
||||||
TEXT = SimpleNamespace()
|
lower_third = SimpleNamespace()
|
||||||
PLAYOUT = SimpleNamespace()
|
playout = SimpleNamespace()
|
||||||
|
|
||||||
INITIAL = SimpleNamespace(load=True)
|
initial = SimpleNamespace(load=True)
|
||||||
FF = SimpleNamespace(decoder=None, encoder=None)
|
ff_proc = SimpleNamespace(decoder=None, encoder=None)
|
||||||
|
|
||||||
|
|
||||||
def str_to_sec(s):
|
def str_to_sec(s):
|
||||||
@ -164,90 +164,90 @@ def load_config():
|
|||||||
some settings cannot be changed - like resolution, aspect, or output
|
some settings cannot be changed - like resolution, aspect, or output
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if STDIN_ARGS.config:
|
if stdin_args.config:
|
||||||
cfg = read_config(STDIN_ARGS.config)
|
cfg = read_config(stdin_args.config)
|
||||||
elif os.path.isfile('/etc/ffplayout/ffplayout.yml'):
|
elif os.path.isfile('/etc/ffplayout/ffplayout.yml'):
|
||||||
cfg = read_config('/etc/ffplayout/ffplayout.yml')
|
cfg = read_config('/etc/ffplayout/ffplayout.yml')
|
||||||
else:
|
else:
|
||||||
cfg = read_config('ffplayout.yml')
|
cfg = read_config('ffplayout.yml')
|
||||||
|
|
||||||
if STDIN_ARGS.start:
|
if stdin_args.start:
|
||||||
p_start = str_to_sec(STDIN_ARGS.start)
|
p_start = str_to_sec(stdin_args.start)
|
||||||
else:
|
else:
|
||||||
p_start = str_to_sec(cfg['playlist']['day_start'])
|
p_start = str_to_sec(cfg['playlist']['day_start'])
|
||||||
|
|
||||||
if p_start is None:
|
if p_start is None:
|
||||||
p_start = get_time('full_sec')
|
p_start = get_time('full_sec')
|
||||||
|
|
||||||
if STDIN_ARGS.length:
|
if stdin_args.length:
|
||||||
p_length = str_to_sec(STDIN_ARGS.length)
|
p_length = str_to_sec(stdin_args.length)
|
||||||
else:
|
else:
|
||||||
p_length = str_to_sec(cfg['playlist']['length'])
|
p_length = str_to_sec(cfg['playlist']['length'])
|
||||||
|
|
||||||
GENERAL.stop = cfg['general']['stop_on_error']
|
sync_op.stop = cfg['general']['stop_on_error']
|
||||||
GENERAL.threshold = cfg['general']['stop_threshold']
|
sync_op.threshold = cfg['general']['stop_threshold']
|
||||||
|
|
||||||
MAIL.subject = cfg['mail']['subject']
|
mail.subject = cfg['mail']['subject']
|
||||||
MAIL.server = cfg['mail']['smpt_server']
|
mail.server = cfg['mail']['smpt_server']
|
||||||
MAIL.port = cfg['mail']['smpt_port']
|
mail.port = cfg['mail']['smpt_port']
|
||||||
MAIL.s_addr = cfg['mail']['sender_addr']
|
mail.s_addr = cfg['mail']['sender_addr']
|
||||||
MAIL.s_pass = cfg['mail']['sender_pass']
|
mail.s_pass = cfg['mail']['sender_pass']
|
||||||
MAIL.recip = cfg['mail']['recipient']
|
mail.recip = cfg['mail']['recipient']
|
||||||
MAIL.level = cfg['mail']['mail_level']
|
mail.level = cfg['mail']['mail_level']
|
||||||
|
|
||||||
PRE.add_logo = cfg['processing']['add_logo']
|
pre.add_logo = cfg['processing']['add_logo']
|
||||||
PRE.logo = cfg['processing']['logo']
|
pre.logo = cfg['processing']['logo']
|
||||||
PRE.logo_scale = cfg['processing']['logo_scale']
|
pre.logo_scale = cfg['processing']['logo_scale']
|
||||||
PRE.logo_filter = cfg['processing']['logo_filter']
|
pre.logo_filter = cfg['processing']['logo_filter']
|
||||||
PRE.logo_opacity = cfg['processing']['logo_opacity']
|
pre.logo_opacity = cfg['processing']['logo_opacity']
|
||||||
PRE.add_loudnorm = cfg['processing']['add_loudnorm']
|
pre.add_loudnorm = cfg['processing']['add_loudnorm']
|
||||||
PRE.loud_i = cfg['processing']['loud_I']
|
pre.loud_i = cfg['processing']['loud_I']
|
||||||
PRE.loud_tp = cfg['processing']['loud_TP']
|
pre.loud_tp = cfg['processing']['loud_TP']
|
||||||
PRE.loud_lra = cfg['processing']['loud_LRA']
|
pre.loud_lra = cfg['processing']['loud_LRA']
|
||||||
PRE.output_count = cfg['processing']['output_count']
|
pre.output_count = cfg['processing']['output_count']
|
||||||
|
|
||||||
PLAYLIST.mode = cfg['playlist']['playlist_mode']
|
playlist.mode = cfg['playlist']['playlist_mode']
|
||||||
PLAYLIST.path = cfg['playlist']['path']
|
playlist.path = cfg['playlist']['path']
|
||||||
PLAYLIST.start = p_start
|
playlist.start = p_start
|
||||||
PLAYLIST.length = p_length
|
playlist.length = p_length
|
||||||
|
|
||||||
STORAGE.path = cfg['storage']['path']
|
storage.path = cfg['storage']['path']
|
||||||
STORAGE.filler = cfg['storage']['filler_clip']
|
storage.filler = cfg['storage']['filler_clip']
|
||||||
STORAGE.extensions = cfg['storage']['extensions']
|
storage.extensions = cfg['storage']['extensions']
|
||||||
STORAGE.shuffle = cfg['storage']['shuffle']
|
storage.shuffle = cfg['storage']['shuffle']
|
||||||
|
|
||||||
TEXT.add_text = cfg['text']['add_text']
|
lower_third.add_text = cfg['text']['add_text']
|
||||||
TEXT.over_pre = cfg['text']['over_pre']
|
lower_third.over_pre = cfg['text']['over_pre']
|
||||||
TEXT.address = cfg['text']['bind_address']
|
lower_third.address = cfg['text']['bind_address']
|
||||||
TEXT.fontfile = cfg['text']['fontfile']
|
lower_third.fontfile = cfg['text']['fontfile']
|
||||||
TEXT.text_from_filename = cfg['text']['text_from_filename']
|
lower_third.text_from_filename = cfg['text']['text_from_filename']
|
||||||
TEXT.style = cfg['text']['style']
|
lower_third.style = cfg['text']['style']
|
||||||
TEXT.regex = cfg['text']['regex']
|
lower_third.regex = cfg['text']['regex']
|
||||||
|
|
||||||
if INITIAL.load:
|
if initial.load:
|
||||||
LOG.to_file = cfg['logging']['log_to_file']
|
log.to_file = cfg['logging']['log_to_file']
|
||||||
LOG.backup_count = cfg['logging']['backup_count']
|
log.backup_count = cfg['logging']['backup_count']
|
||||||
LOG.path = cfg['logging']['log_path']
|
log.path = cfg['logging']['log_path']
|
||||||
LOG.level = cfg['logging']['log_level']
|
log.level = cfg['logging']['log_level']
|
||||||
LOG.ff_level = cfg['logging']['ffmpeg_level']
|
log.ff_level = cfg['logging']['ffmpeg_level']
|
||||||
|
|
||||||
PRE.w = cfg['processing']['width']
|
pre.w = cfg['processing']['width']
|
||||||
PRE.h = cfg['processing']['height']
|
pre.h = cfg['processing']['height']
|
||||||
PRE.aspect = cfg['processing']['aspect']
|
pre.aspect = cfg['processing']['aspect']
|
||||||
PRE.fps = cfg['processing']['fps']
|
pre.fps = cfg['processing']['fps']
|
||||||
PRE.v_bitrate = cfg['processing']['width'] * \
|
pre.v_bitrate = cfg['processing']['width'] * \
|
||||||
cfg['processing']['height'] / 10
|
cfg['processing']['height'] / 10
|
||||||
PRE.v_bufsize = PRE.v_bitrate / 2
|
pre.v_bufsize = pre.v_bitrate / 2
|
||||||
PRE.realtime = cfg['processing']['use_realtime']
|
pre.realtime = cfg['processing']['use_realtime']
|
||||||
|
|
||||||
PLAYOUT.mode = cfg['out']['mode']
|
playout.mode = cfg['out']['mode']
|
||||||
PLAYOUT.name = cfg['out']['service_name']
|
playout.name = cfg['out']['service_name']
|
||||||
PLAYOUT.provider = cfg['out']['service_provider']
|
playout.provider = cfg['out']['service_provider']
|
||||||
PLAYOUT.ffmpeg_param = cfg['out']['ffmpeg_param'].split(' ')
|
playout.ffmpeg_param = cfg['out']['ffmpeg_param'].split(' ')
|
||||||
PLAYOUT.stream_output = cfg['out']['stream_output'].split(' ')
|
playout.stream_output = cfg['out']['stream_output'].split(' ')
|
||||||
PLAYOUT.hls_output = cfg['out']['hls_output'].split(' ')
|
playout.hls_output = cfg['out']['hls_output'].split(' ')
|
||||||
|
|
||||||
INITIAL.load = False
|
initial.load = False
|
||||||
|
|
||||||
|
|
||||||
load_config()
|
load_config()
|
||||||
@ -308,21 +308,21 @@ class CustomFormatter(logging.Formatter):
|
|||||||
|
|
||||||
|
|
||||||
# If the log file is specified on the command line then override the default
|
# If the log file is specified on the command line then override the default
|
||||||
if STDIN_ARGS.log:
|
if stdin_args.log:
|
||||||
LOG.path = STDIN_ARGS.log
|
log.path = stdin_args.log
|
||||||
|
|
||||||
playout_logger = logging.getLogger('playout')
|
playout_logger = logging.getLogger('playout')
|
||||||
playout_logger.setLevel(LOG.level)
|
playout_logger.setLevel(log.level)
|
||||||
decoder_logger = logging.getLogger('decoder')
|
decoder_logger = logging.getLogger('decoder')
|
||||||
decoder_logger.setLevel(LOG.ff_level)
|
decoder_logger.setLevel(log.ff_level)
|
||||||
encoder_logger = logging.getLogger('encoder')
|
encoder_logger = logging.getLogger('encoder')
|
||||||
encoder_logger.setLevel(LOG.ff_level)
|
encoder_logger.setLevel(log.ff_level)
|
||||||
|
|
||||||
if LOG.to_file and LOG.path != 'none':
|
if log.to_file and log.path != 'none':
|
||||||
if LOG.path and os.path.isdir(LOG.path):
|
if log.path and os.path.isdir(log.path):
|
||||||
playout_log = os.path.join(LOG.path, 'ffplayout.log')
|
playout_log = os.path.join(log.path, 'ffplayout.log')
|
||||||
decoder_log = os.path.join(LOG.path, 'decoder.log')
|
decoder_log = os.path.join(log.path, 'decoder.log')
|
||||||
encoder_log = os.path.join(LOG.path, 'encoder.log')
|
encoder_log = os.path.join(log.path, 'encoder.log')
|
||||||
else:
|
else:
|
||||||
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
log_dir = os.path.join(base_dir, 'log')
|
log_dir = os.path.join(base_dir, 'log')
|
||||||
@ -334,11 +334,11 @@ if LOG.to_file and LOG.path != 'none':
|
|||||||
p_format = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s')
|
p_format = logging.Formatter('[%(asctime)s] [%(levelname)s] %(message)s')
|
||||||
f_format = logging.Formatter('[%(asctime)s] %(message)s')
|
f_format = logging.Formatter('[%(asctime)s] %(message)s')
|
||||||
p_file_handler = TimedRotatingFileHandler(playout_log, when='midnight',
|
p_file_handler = TimedRotatingFileHandler(playout_log, when='midnight',
|
||||||
backupCount=LOG.backup_count)
|
backupCount=log.backup_count)
|
||||||
d_file_handler = TimedRotatingFileHandler(decoder_log, when='midnight',
|
d_file_handler = TimedRotatingFileHandler(decoder_log, when='midnight',
|
||||||
backupCount=LOG.backup_count)
|
backupCount=log.backup_count)
|
||||||
e_file_handler = TimedRotatingFileHandler(encoder_log, when='midnight',
|
e_file_handler = TimedRotatingFileHandler(encoder_log, when='midnight',
|
||||||
backupCount=LOG.backup_count)
|
backupCount=log.backup_count)
|
||||||
|
|
||||||
p_file_handler.setFormatter(p_format)
|
p_file_handler.setFormatter(p_format)
|
||||||
d_file_handler.setFormatter(f_format)
|
d_file_handler.setFormatter(f_format)
|
||||||
@ -370,7 +370,7 @@ class Mailer:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.level = MAIL.level
|
self.level = mail.level
|
||||||
self.time = None
|
self.time = None
|
||||||
self.timestamp = get_time('stamp')
|
self.timestamp = get_time('stamp')
|
||||||
self.rate_limit = 600
|
self.rate_limit = 600
|
||||||
@ -380,7 +380,7 @@ class Mailer:
|
|||||||
self.time = get_time(None)
|
self.time = get_time(None)
|
||||||
|
|
||||||
def send_mail(self, msg):
|
def send_mail(self, msg):
|
||||||
if MAIL.recip:
|
if mail.recip:
|
||||||
# write message to temp file for rate limit
|
# write message to temp file for rate limit
|
||||||
with open(self.temp_msg, 'w+') as f:
|
with open(self.temp_msg, 'w+') as f:
|
||||||
f.write(msg)
|
f.write(msg)
|
||||||
@ -388,15 +388,15 @@ class Mailer:
|
|||||||
self.current_time()
|
self.current_time()
|
||||||
|
|
||||||
message = MIMEMultipart()
|
message = MIMEMultipart()
|
||||||
message['From'] = MAIL.s_addr
|
message['From'] = mail.s_addr
|
||||||
message['To'] = MAIL.recip
|
message['To'] = mail.recip
|
||||||
message['Subject'] = MAIL.subject
|
message['Subject'] = mail.subject
|
||||||
message['Date'] = formatdate(localtime=True)
|
message['Date'] = formatdate(localtime=True)
|
||||||
message.attach(MIMEText(f'{self.time} {msg}', 'plain'))
|
message.attach(MIMEText(f'{self.time} {msg}', 'plain'))
|
||||||
text = message.as_string()
|
text = message.as_string()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server = smtplib.SMTP(MAIL.server, MAIL.port)
|
server = smtplib.SMTP(mail.server, mail.port)
|
||||||
except socket.error as err:
|
except socket.error as err:
|
||||||
playout_logger.error(err)
|
playout_logger.error(err)
|
||||||
server = None
|
server = None
|
||||||
@ -404,14 +404,14 @@ class Mailer:
|
|||||||
if server is not None:
|
if server is not None:
|
||||||
server.starttls()
|
server.starttls()
|
||||||
try:
|
try:
|
||||||
login = server.login(MAIL.s_addr, MAIL.s_pass)
|
login = server.login(mail.s_addr, mail.s_pass)
|
||||||
except smtplib.SMTPAuthenticationError as serr:
|
except smtplib.SMTPAuthenticationError as serr:
|
||||||
playout_logger.error(serr)
|
playout_logger.error(serr)
|
||||||
login = None
|
login = None
|
||||||
|
|
||||||
if login is not None:
|
if login is not None:
|
||||||
server.sendmail(MAIL.s_addr,
|
server.sendmail(mail.s_addr,
|
||||||
re.split(', |; |,|;', MAIL.recip), text)
|
re.split(', |; |,|;', mail.recip), text)
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
||||||
def check_if_new(self, msg):
|
def check_if_new(self, msg):
|
||||||
@ -623,11 +623,11 @@ def terminate_processes(watcher=None):
|
|||||||
"""
|
"""
|
||||||
kill orphaned processes
|
kill orphaned processes
|
||||||
"""
|
"""
|
||||||
if FF.decoder and FF.decoder.poll() is None:
|
if ff_proc.decoder and ff_proc.decoder.poll() is None:
|
||||||
FF.decoder.terminate()
|
ff_proc.decoder.terminate()
|
||||||
|
|
||||||
if FF.encoder and FF.encoder.poll() is None:
|
if ff_proc.encoder and ff_proc.encoder.poll() is None:
|
||||||
FF.encoder.terminate()
|
ff_proc.encoder.terminate()
|
||||||
|
|
||||||
if watcher:
|
if watcher:
|
||||||
watcher.stop()
|
watcher.stop()
|
||||||
@ -647,9 +647,9 @@ def ffmpeg_stderr_reader(std_errors, decoder):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
for line in std_errors:
|
for line in std_errors:
|
||||||
if LOG.ff_level == 'INFO':
|
if log.ff_level == 'INFO':
|
||||||
logger.info(f'{prefix}{line.decode("utf-8").rstrip()}')
|
logger.info(f'{prefix}{line.decode("utf-8").rstrip()}')
|
||||||
elif LOG.ff_level == 'WARNING':
|
elif log.ff_level == 'WARNING':
|
||||||
logger.warning(f'{prefix}{line.decode("utf-8").rstrip()}')
|
logger.warning(f'{prefix}{line.decode("utf-8").rstrip()}')
|
||||||
else:
|
else:
|
||||||
logger.error(f'{prefix}{line.decode("utf-8").rstrip()}')
|
logger.error(f'{prefix}{line.decode("utf-8").rstrip()}')
|
||||||
@ -663,17 +663,17 @@ def get_delta(begin):
|
|||||||
"""
|
"""
|
||||||
current_time = get_time('full_sec')
|
current_time = get_time('full_sec')
|
||||||
|
|
||||||
if STDIN_ARGS.length and str_to_sec(STDIN_ARGS.length):
|
if stdin_args.length and str_to_sec(stdin_args.length):
|
||||||
target_playtime = str_to_sec(STDIN_ARGS.length)
|
target_playtime = str_to_sec(stdin_args.length)
|
||||||
elif PLAYLIST.length:
|
elif playlist.length:
|
||||||
target_playtime = PLAYLIST.length
|
target_playtime = playlist.length
|
||||||
else:
|
else:
|
||||||
target_playtime = 86400.0
|
target_playtime = 86400.0
|
||||||
|
|
||||||
if begin == PLAYLIST.start == 0 and 86400.0 - current_time < 4:
|
if begin == playlist.start == 0 and 86400.0 - current_time < 4:
|
||||||
current_time -= target_playtime
|
current_time -= target_playtime
|
||||||
|
|
||||||
elif PLAYLIST.start >= current_time and not begin == PLAYLIST.start:
|
elif playlist.start >= current_time and not begin == playlist.start:
|
||||||
current_time += target_playtime
|
current_time += target_playtime
|
||||||
|
|
||||||
current_delta = begin - current_time
|
current_delta = begin - current_time
|
||||||
@ -681,7 +681,7 @@ def get_delta(begin):
|
|||||||
if math.isclose(current_delta, 86400.0, abs_tol=6):
|
if math.isclose(current_delta, 86400.0, abs_tol=6):
|
||||||
current_delta -= 86400.0
|
current_delta -= 86400.0
|
||||||
|
|
||||||
ref_time = target_playtime + PLAYLIST.start
|
ref_time = target_playtime + playlist.start
|
||||||
total_delta = ref_time - begin + current_delta
|
total_delta = ref_time - begin + current_delta
|
||||||
|
|
||||||
return current_delta, total_delta
|
return current_delta, total_delta
|
||||||
@ -695,9 +695,9 @@ def get_date(seek_day, next_start=0):
|
|||||||
"""
|
"""
|
||||||
d = date.today()
|
d = date.today()
|
||||||
|
|
||||||
if seek_day and PLAYLIST.start > get_time('full_sec'):
|
if seek_day and playlist.start > get_time('full_sec'):
|
||||||
return (d - timedelta(1)).strftime('%Y-%m-%d')
|
return (d - timedelta(1)).strftime('%Y-%m-%d')
|
||||||
elif PLAYLIST.start == 0 and next_start >= 86400:
|
elif playlist.start == 0 and next_start >= 86400:
|
||||||
return (d + timedelta(1)).strftime('%Y-%m-%d')
|
return (d + timedelta(1)).strftime('%Y-%m-%d')
|
||||||
else:
|
else:
|
||||||
return d.strftime('%Y-%m-%d')
|
return d.strftime('%Y-%m-%d')
|
||||||
@ -738,12 +738,12 @@ def check_sync(delta):
|
|||||||
check that we are in tolerance time
|
check that we are in tolerance time
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if PLAYLIST.mode and PLAYLIST.start and PLAYLIST.length:
|
if playlist.mode and playlist.start and playlist.length:
|
||||||
# save time delta to global variable for syncing
|
# save time delta to global variable for syncing
|
||||||
# this is needed for real time filter
|
# this is needed for real time filter
|
||||||
GENERAL.time_delta = delta
|
sync_op.time_delta = delta
|
||||||
|
|
||||||
if GENERAL.stop and abs(delta) > GENERAL.threshold:
|
if sync_op.stop and abs(delta) > sync_op.threshold:
|
||||||
messenger.error(
|
messenger.error(
|
||||||
f'Sync tolerance value exceeded with {delta:.2f} seconds,\n'
|
f'Sync tolerance value exceeded with {delta:.2f} seconds,\n'
|
||||||
'program terminated!')
|
'program terminated!')
|
||||||
@ -785,7 +785,7 @@ def gen_dummy(duration):
|
|||||||
# noise = 'noise=alls=50:allf=t+u,hue=s=0'
|
# noise = 'noise=alls=50:allf=t+u,hue=s=0'
|
||||||
return [
|
return [
|
||||||
'-f', 'lavfi', '-i',
|
'-f', 'lavfi', '-i',
|
||||||
f'color=c={color}:s={PRE.w}x{PRE.h}:d={duration}:r={PRE.fps},'
|
f'color=c={color}:s={pre.w}x{pre.h}:d={duration}:r={pre.fps},'
|
||||||
'format=pix_fmts=yuv420p',
|
'format=pix_fmts=yuv420p',
|
||||||
'-f', 'lavfi', '-i', f'anoisesrc=d={duration}:c=pink:r=48000:a=0.05'
|
'-f', 'lavfi', '-i', f'anoisesrc=d={duration}:c=pink:r=48000:a=0.05'
|
||||||
]
|
]
|
||||||
@ -796,7 +796,7 @@ def gen_filler(node):
|
|||||||
generate filler clip to fill empty space in playlist
|
generate filler clip to fill empty space in playlist
|
||||||
"""
|
"""
|
||||||
probe = MediaProbe()
|
probe = MediaProbe()
|
||||||
probe.load(STORAGE.filler)
|
probe.load(storage.filler)
|
||||||
duration = node['out'] - node['seek']
|
duration = node['out'] - node['seek']
|
||||||
|
|
||||||
node['probe'] = probe
|
node['probe'] = probe
|
||||||
@ -808,13 +808,13 @@ def gen_filler(node):
|
|||||||
# cut filler
|
# cut filler
|
||||||
messenger.info(
|
messenger.info(
|
||||||
f'Generate filler with {duration:.2f} seconds')
|
f'Generate filler with {duration:.2f} seconds')
|
||||||
node['source'] = STORAGE.filler
|
node['source'] = storage.filler
|
||||||
node['src_cmd'] = ['-i', STORAGE.filler] + set_length(
|
node['src_cmd'] = ['-i', storage.filler] + set_length(
|
||||||
filler_duration, 0, duration)
|
filler_duration, 0, duration)
|
||||||
return node
|
return node
|
||||||
else:
|
else:
|
||||||
# loop file n times
|
# loop file n times
|
||||||
node['src_cmd'] = loop_input(STORAGE.filler, filler_duration,
|
node['src_cmd'] = loop_input(storage.filler, filler_duration,
|
||||||
duration)
|
duration)
|
||||||
return node
|
return node
|
||||||
else:
|
else:
|
||||||
@ -881,7 +881,7 @@ def pre_audio_codec():
|
|||||||
s302m has higher quality, but is experimental
|
s302m has higher quality, but is experimental
|
||||||
and works not well together with the loudnorm filter
|
and works not well together with the loudnorm filter
|
||||||
"""
|
"""
|
||||||
if PRE.add_loudnorm:
|
if pre.add_loudnorm:
|
||||||
return ['-c:a', 'mp2', '-b:a', '384k', '-ar', '48000', '-ac', '2']
|
return ['-c:a', 'mp2', '-b:a', '384k', '-ar', '48000', '-ac', '2']
|
||||||
else:
|
else:
|
||||||
return ['-c:a', 's302m', '-strict', '-2', '-ar', '48000', '-ac', '2']
|
return ['-c:a', 's302m', '-strict', '-2', '-ar', '48000', '-ac', '2']
|
||||||
|
@ -98,37 +98,37 @@ def run_with_no_elements(time_tuple):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from ffplayout.output import desktop
|
from ffplayout.output import desktop
|
||||||
from ffplayout.utils import PLAYLIST, terminate_processes
|
from ffplayout.utils import playlist, terminate_processes
|
||||||
|
|
||||||
print('\ntest playlists, which are empty')
|
print('\ntest playlists, which are empty')
|
||||||
PLAYLIST.start = 0
|
playlist.start = 0
|
||||||
run_time(140)
|
run_time(140)
|
||||||
run_with_no_elements((2021, 2, 15, 23, 59, 53))
|
run_with_no_elements((2021, 2, 15, 23, 59, 53))
|
||||||
|
|
||||||
print_separater()
|
print_separater()
|
||||||
|
|
||||||
print('\ntest playlists, which are to short')
|
print('\ntest playlists, which are to short')
|
||||||
PLAYLIST.start = 0
|
playlist.start = 0
|
||||||
run_time(140)
|
run_time(140)
|
||||||
run_with_less_elements((2021, 2, 15, 23, 58, 3))
|
run_with_less_elements((2021, 2, 15, 23, 58, 3))
|
||||||
|
|
||||||
print_separater()
|
print_separater()
|
||||||
|
|
||||||
print('\ntest playlists, which are to long')
|
print('\ntest playlists, which are to long')
|
||||||
PLAYLIST.start = 0
|
playlist.start = 0
|
||||||
run_time(140)
|
run_time(140)
|
||||||
run_with_more_elements((2021, 2, 15, 23, 59, 33))
|
run_with_more_elements((2021, 2, 15, 23, 59, 33))
|
||||||
|
|
||||||
print_separater()
|
print_separater()
|
||||||
|
|
||||||
print('\ntest transition from playlists, with day_start at: 05:59:25')
|
print('\ntest transition from playlists, with day_start at: 05:59:25')
|
||||||
PLAYLIST.start = 21575
|
playlist.start = 21575
|
||||||
run_time(140)
|
run_time(140)
|
||||||
run_at((2021, 2, 17, 5, 58, 3))
|
run_at((2021, 2, 17, 5, 58, 3))
|
||||||
|
|
||||||
print_separater()
|
print_separater()
|
||||||
|
|
||||||
print('\ntest transition from playlists, with day_start at: 20:00:00')
|
print('\ntest transition from playlists, with day_start at: 20:00:00')
|
||||||
PLAYLIST.start = 72000
|
playlist.start = 72000
|
||||||
run_time(140)
|
run_time(140)
|
||||||
run_at((2021, 2, 17, 19, 58, 23))
|
run_at((2021, 2, 17, 19, 58, 23))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user