Add local fallback mode

Fall back to using the local ffmpeg binary (at the same path as the
remote system) if we are unable to reach a remote server. Prevents
rffmpeg from getting stuck if it can find no valid remote servers.
This commit is contained in:
Joshua M. Boniface 2020-06-30 18:16:39 -04:00
parent 1e51fccba4
commit 4cc50686ea
2 changed files with 49 additions and 2 deletions

View file

@ -28,6 +28,12 @@ rffmpeg supports setting multiple hosts. It keeps state in `/run/shm/rffmpeg`, o
When running rffmpeg manually, *do not* exit it with `Ctrl+C`. Doing so will likely leave the `ffmpeg` process running on the remote machine. Instead, enter `q` and a newline ("Enter") into the rffmpeg process, and this will terminate the entire command cleanly. This is the method that Jellyfin uses to communicate the termination of an `ffmpeg` process.
### Local fallback
rffmpeg will fall back to a local copy of ffmpeg, at the same location as on remote systems (i.e. as configured in `/etc/rffmpeg/rffmpeg.yml`), should it be unable to find any working remote hosts. This helps prevent situations where rffmpeg cannot be run due to none of the remote host(s) being unavailable.
If you want the local system to be included in the normal list, for instance if the local system is also a powerful transcode machine, you can add `localhost` to the list of hosts in order to have it be used along with the remote systems; it will SSH to itself but, if the guide below is followed exactly, will work as expected.
## Full setup guide
This example setup is the one I use for `rffmpeg`, involving a media server (`jf1`) and a remote transcode server (`gpu1`). Both systems run Debian GNU/Linux, though the commands below should also work on Ubuntu. Note that Docker is not officially supported with `rffmpeg` due to the complexity of exporting Docker volumes with NFS, the path differences, and the fact that I don't use Docker, but if you do figure it out a PR is welcome.

View file

@ -95,6 +95,47 @@ current_statefile = config['state_tempdir'] + '/' + config['state_filename'].for
logger("Starting rffmpeg {}: {}".format(our_pid, ' '.join(all_args)))
def local_ffmpeg_fallback():
"""
Fallback call to local ffmpeg
"""
rffmpeg_command = list()
# Prepare our default stdin/stdout/stderr (normally, stdout to stderr)
stdin = sys.stdin
stdout = sys.stderr
stderr = sys.stderr
# Verify if we're in ffmpeg or ffprobe mode
if 'ffprobe' in all_args[0]:
rffmpeg_command.append(config['ffprobe_command'])
stdout = sys.stdout
else:
rffmpeg_command.append(config['ffmpeg_command'])
# Determine if version, encorders, or decoders is an argument; if so, we output stdout to stdout
# Weird workaround for something Jellyfin requires...
if '-version' in cli_ffmpeg_args or '-encoders' in cli_ffmpeg_args or '-decoders' in cli_ffmpeg_args:
stdout = sys.stdout
# Parse and re-quote any problematic arguments
for arg in cli_ffmpeg_args:
rffmpeg_command.append('{}'.format(arg))
p = subprocess.run(rffmpeg_command,
shell=False,
bufsize=0,
universal_newlines=True,
stdin=stdin,
stderr=stderr,
stdout=stdout)
returncode = p.returncode
os.remove(current_statefile)
logger("Finished rffmpeg {} (local failover mode) with return code {}".format(our_pid, returncode))
exit(returncode)
def get_target_host():
"""
Determine the optimal target host
@ -147,8 +188,8 @@ def get_target_host():
target_host = host
if not target_host:
logger('ERROR: Failed to find a valid target host')
exit(1)
logger('Failed to find a valid target host - using local fallback instead')
local_ffmpeg_fallback()
# Write to our state file
with open(current_statefile, 'a') as statefile: