118 lines
3.4 KiB
Python
118 lines
3.4 KiB
Python
import logging
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from functools import lru_cache
|
|
|
|
from pkg_resources import resource_filename
|
|
|
|
from ._definitions import FNAME_PER_PLATFORM, get_platform
|
|
|
|
logger = logging.getLogger("imageio_ffmpeg")
|
|
|
|
|
|
def get_ffmpeg_exe():
|
|
"""
|
|
Get the ffmpeg executable file. This can be the binary defined by
|
|
the IMAGEIO_FFMPEG_EXE environment variable, the binary distributed
|
|
with imageio-ffmpeg, an ffmpeg binary installed with conda, or the
|
|
system ffmpeg (in that order). A RuntimeError is raised if no valid
|
|
ffmpeg could be found.
|
|
"""
|
|
|
|
# 1. Try environment variable. - Dont test it: the user is explicit here!
|
|
exe = os.getenv("IMAGEIO_FFMPEG_EXE", None)
|
|
if exe:
|
|
return exe
|
|
|
|
# Auto-detect
|
|
exe = _get_ffmpeg_exe()
|
|
if exe:
|
|
return exe
|
|
|
|
# Nothing was found
|
|
raise RuntimeError(
|
|
"No ffmpeg exe could be found. Install ffmpeg on your system, "
|
|
"or set the IMAGEIO_FFMPEG_EXE environment variable."
|
|
)
|
|
|
|
|
|
@lru_cache()
|
|
def _get_ffmpeg_exe():
|
|
plat = get_platform()
|
|
|
|
# 2. Try from here
|
|
bin_dir = resource_filename("imageio_ffmpeg", "binaries")
|
|
exe = os.path.join(bin_dir, FNAME_PER_PLATFORM.get(plat, ""))
|
|
if exe and os.path.isfile(exe) and _is_valid_exe(exe):
|
|
return exe
|
|
|
|
# 3. Try binary from conda package
|
|
# (installed e.g. via `conda install ffmpeg -c conda-forge`)
|
|
if plat.startswith("win"):
|
|
exe = os.path.join(sys.prefix, "Library", "bin", "ffmpeg.exe")
|
|
else:
|
|
exe = os.path.join(sys.prefix, "bin", "ffmpeg")
|
|
if exe and os.path.isfile(exe) and _is_valid_exe(exe):
|
|
return exe
|
|
|
|
# 4. Try system ffmpeg command
|
|
exe = "ffmpeg"
|
|
if _is_valid_exe(exe):
|
|
return exe
|
|
|
|
return None
|
|
|
|
|
|
def _popen_kwargs(prevent_sigint=False):
|
|
startupinfo = None
|
|
preexec_fn = None
|
|
creationflags = 0
|
|
if sys.platform.startswith("win"):
|
|
# Stops executable from flashing on Windows (see #22)
|
|
startupinfo = subprocess.STARTUPINFO()
|
|
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
if prevent_sigint:
|
|
# Prevent propagation of sigint (see #4)
|
|
# https://stackoverflow.com/questions/5045771
|
|
if sys.platform.startswith("win"):
|
|
creationflags = 0x00000200
|
|
else:
|
|
preexec_fn = os.setpgrp # the _pre_exec does not seem to work
|
|
|
|
falsy = ("", "0", "false", "no")
|
|
if os.getenv("IMAGEIO_FFMPEG_NO_PREVENT_SIGINT", "").lower() not in falsy:
|
|
# Unset preexec_fn to work around a strange hang on fork() (see #58)
|
|
preexec_fn = None
|
|
|
|
return {
|
|
"startupinfo": startupinfo,
|
|
"creationflags": creationflags,
|
|
"preexec_fn": preexec_fn,
|
|
}
|
|
|
|
|
|
def _is_valid_exe(exe):
|
|
cmd = [exe, "-version"]
|
|
try:
|
|
with open(os.devnull, "w") as null:
|
|
subprocess.check_call(
|
|
cmd, stdout=null, stderr=subprocess.STDOUT, **_popen_kwargs()
|
|
)
|
|
return True
|
|
except (OSError, ValueError, subprocess.CalledProcessError):
|
|
return False
|
|
|
|
|
|
def get_ffmpeg_version():
|
|
"""
|
|
Get the version of the used ffmpeg executable (as a string).
|
|
"""
|
|
exe = get_ffmpeg_exe()
|
|
line = subprocess.check_output([exe, "-version"], **_popen_kwargs()).split(
|
|
b"\n", 1
|
|
)[0]
|
|
line = line.decode(errors="ignore").strip()
|
|
version = line.split("version", 1)[-1].lstrip().split(" ", 1)[0].strip()
|
|
return version
|