commit
085ff865bb
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [jb-alvarado]
|
@ -16,7 +16,7 @@ Features
|
|||||||
- playing clips from [watched folder](https://github.com/ffplayout/ffplayout-engine/wiki/Watch-Folder)
|
- playing clips from [watched folder](https://github.com/ffplayout/ffplayout-engine/wiki/Watch-Folder)
|
||||||
- send emails with error message
|
- send emails with error message
|
||||||
- overlay a logo
|
- overlay a logo
|
||||||
- overlay scrolling text
|
- overlay text, controllable through [messenger](https://github.com/ffplayout/messenger) over libzmq
|
||||||
- **EBU R128 loudness** normalization (single pass) (experimental)
|
- **EBU R128 loudness** normalization (single pass) (experimental)
|
||||||
- loop clip in playlist which `out` value is higher then its `duration`, see also [Loop Clip](https://github.com/ffplayout/ffplayout-engine/wiki/Loop-Clip)
|
- loop clip in playlist which `out` value is higher then its `duration`, see also [Loop Clip](https://github.com/ffplayout/ffplayout-engine/wiki/Loop-Clip)
|
||||||
- loop playlist infinitely
|
- loop playlist infinitely
|
||||||
@ -31,7 +31,7 @@ Features
|
|||||||
- add filters to input, if is necessary to match output stream:
|
- add filters to input, if is necessary to match output stream:
|
||||||
- **yadif** (deinterlacing)
|
- **yadif** (deinterlacing)
|
||||||
- **pad** (letterbox or pillarbox to fit aspect)
|
- **pad** (letterbox or pillarbox to fit aspect)
|
||||||
- **framerate** (change fps)
|
- **fps** (change fps)
|
||||||
- **scale** (fit target resolution)
|
- **scale** (fit target resolution)
|
||||||
- **aevalsrc** (if video have no audio)
|
- **aevalsrc** (if video have no audio)
|
||||||
- **apad** (add silence if audio duration is to short)
|
- **apad** (add silence if audio duration is to short)
|
||||||
@ -44,6 +44,7 @@ Requirements
|
|||||||
- python module **watchdog** (only when `playlist_mode = False`)
|
- python module **watchdog** (only when `playlist_mode = False`)
|
||||||
- python module **colorama** if you are on windows
|
- python module **colorama** if you are on windows
|
||||||
- **ffmpeg v4.2+** and **ffprobe** (**ffplay** if you want to play on desktop)
|
- **ffmpeg v4.2+** and **ffprobe** (**ffplay** if you want to play on desktop)
|
||||||
|
- if you want to overlay text, ffmpeg needs to have **libzmq**
|
||||||
- RAM and CPU depends on video resolution, minimum 4 threads and 3GB RAM for 720p are recommend
|
- RAM and CPU depends on video resolution, minimum 4 threads and 3GB RAM for 720p are recommend
|
||||||
|
|
||||||
JSON Playlist Example
|
JSON Playlist Example
|
||||||
|
@ -110,23 +110,13 @@ extensions = ["*.mp4"]
|
|||||||
shuffle = False
|
shuffle = False
|
||||||
|
|
||||||
|
|
||||||
; overlay text
|
; overlay text in combination with messenger: https://github.com/ffplayout/messenger
|
||||||
; for paramters check ffmpeg doc
|
|
||||||
; leave textfile blank when you don't need this
|
|
||||||
; in some systems decimal point is a comma, have this in mind when ffmpeg complains about wrong values
|
|
||||||
; on windows fontfile path need to be like this: C\:/WINDOWS/fonts/DejaVuSans.ttf
|
; on windows fontfile path need to be like this: C\:/WINDOWS/fonts/DejaVuSans.ttf
|
||||||
; textfile has the same pattern
|
; in a standard environment the filter drawtext node is: Parsed_drawtext_2
|
||||||
[TEXT]
|
[TEXT]
|
||||||
add_text = True
|
add_text = True
|
||||||
textfile = /media/live.txt
|
bind_address = tcp://127.0.0.1:5555
|
||||||
fontsize = 24
|
|
||||||
fontcolor = white
|
|
||||||
fontfile = /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
|
fontfile = /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
|
||||||
box = 1
|
|
||||||
boxcolor = black@0xbb
|
|
||||||
boxborderw = 6
|
|
||||||
x = w-w/8*mod(t,8*(w+tw)/w)
|
|
||||||
y = (h-line_h)*0.9
|
|
||||||
|
|
||||||
|
|
||||||
; the final playout post compression
|
; the final playout post compression
|
||||||
|
36
ffplayout.py
36
ffplayout.py
@ -212,15 +212,8 @@ def load_config():
|
|||||||
_storage.shuffle = cfg.getboolean('STORAGE', 'shuffle')
|
_storage.shuffle = cfg.getboolean('STORAGE', 'shuffle')
|
||||||
|
|
||||||
_text.add_text = cfg.getboolean('TEXT', 'add_text')
|
_text.add_text = cfg.getboolean('TEXT', 'add_text')
|
||||||
_text.textfile = cfg.get('TEXT', 'textfile')
|
_text.address = cfg.get('TEXT', 'bind_address')
|
||||||
_text.fontsize = cfg.get('TEXT', 'fontsize')
|
|
||||||
_text.fontcolor = cfg.get('TEXT', 'fontcolor')
|
|
||||||
_text.fontfile = cfg.get('TEXT', 'fontfile')
|
_text.fontfile = cfg.get('TEXT', 'fontfile')
|
||||||
_text.box = cfg.get('TEXT', 'box')
|
|
||||||
_text.boxcolor = cfg.get('TEXT', 'boxcolor')
|
|
||||||
_text.boxborderw = cfg.get('TEXT', 'boxborderw')
|
|
||||||
_text.x = cfg.get('TEXT', 'x')
|
|
||||||
_text.y = cfg.get('TEXT', 'y')
|
|
||||||
|
|
||||||
if _init.load:
|
if _init.load:
|
||||||
_log.to_file = cfg.getboolean('LOGGING', 'log_to_file')
|
_log.to_file = cfg.getboolean('LOGGING', 'log_to_file')
|
||||||
@ -909,14 +902,21 @@ def handle_list_end(probe, new_length, src, begin, dur, seek, out):
|
|||||||
messenger.info(
|
messenger.info(
|
||||||
'We are over time, new length is: {0:.2f}'.format(new_length))
|
'We are over time, new length is: {0:.2f}'.format(new_length))
|
||||||
|
|
||||||
if dur > new_length > 1.5:
|
missing_secs = abs(new_length - (dur - seek))
|
||||||
|
|
||||||
|
if dur > new_length > 1.5 and dur - seek >= new_length:
|
||||||
src_cmd = src_or_dummy(probe, src, dur, seek, new_out)
|
src_cmd = src_or_dummy(probe, src, dur, seek, new_out)
|
||||||
elif dur > new_length > 0.0:
|
elif dur > new_length > 0.0:
|
||||||
messenger.info(
|
messenger.info(
|
||||||
'Last clip less then 1.5 second long, skip:\n{}'.format(src))
|
'Last clip less then 1.5 second long, skip:\n{}'.format(src))
|
||||||
src_cmd = None
|
src_cmd = None
|
||||||
|
|
||||||
|
if missing_secs > 2:
|
||||||
|
new_playlist = False
|
||||||
|
messenger.error(
|
||||||
|
'Reach playlist end,\n{0:.2f} seconds needed.'.format(
|
||||||
|
missing_secs))
|
||||||
else:
|
else:
|
||||||
missing_secs = abs(new_length - dur)
|
|
||||||
new_out = out
|
new_out = out
|
||||||
new_playlist = False
|
new_playlist = False
|
||||||
src_cmd = src_or_dummy(probe, src, dur, seek, out)
|
src_cmd = src_or_dummy(probe, src, dur, seek, out)
|
||||||
@ -1587,15 +1587,13 @@ def main():
|
|||||||
'-bufsize', '{}k'.format(_pre_comp.v_bufsize)
|
'-bufsize', '{}k'.format(_pre_comp.v_bufsize)
|
||||||
] + pre_audio_codec() + ['-f', 'mpegts', '-']
|
] + pre_audio_codec() + ['-f', 'mpegts', '-']
|
||||||
|
|
||||||
if _text.add_text and os.path.isfile(_text.textfile):
|
if _text.add_text:
|
||||||
messenger.info('Overlay text file: "{}"'.format(_text.textfile))
|
messenger.info('Using drawtext node, listening on address: {}'.format(
|
||||||
|
_text.address
|
||||||
|
))
|
||||||
overlay = [
|
overlay = [
|
||||||
'-vf', ("drawtext=box={}:boxcolor='{}':boxborderw={}"
|
'-vf', "null,zmq=b='{}',drawtext=text='':fontfile='{}'".format(
|
||||||
":fontsize={}:fontcolor={}:fontfile='{}':textfile={}"
|
_text.address.replace(':', '\\:'), _text.fontfile)
|
||||||
":reload=1:x='{}':y='{}'").format(
|
|
||||||
_text.box, _text.boxcolor, _text.boxborderw,
|
|
||||||
_text.fontsize, _text.fontcolor, _text.fontfile,
|
|
||||||
_text.textfile, _text.x, _text.y)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1676,6 +1674,8 @@ def main():
|
|||||||
_ff.encoder.terminate()
|
_ff.encoder.terminate()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
if _ff.encoder.poll() is None:
|
||||||
|
_ff.encoder.terminate()
|
||||||
_ff.encoder.wait()
|
_ff.encoder.wait()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user