[ffmpeg] Framework for feature detection
Related: #1502, #1237, https://github.com/ytdl-org/youtube-dl/pull/29581
This commit is contained in:
parent
31c49255bf
commit
9af98e17bd
3 changed files with 54 additions and 41 deletions
|
@ -232,7 +232,8 @@ def _real_main(argv=None):
|
||||||
parser.error('invalid audio format specified')
|
parser.error('invalid audio format specified')
|
||||||
if opts.audioquality:
|
if opts.audioquality:
|
||||||
opts.audioquality = opts.audioquality.strip('k').strip('K')
|
opts.audioquality = opts.audioquality.strip('k').strip('K')
|
||||||
if int_or_none(float_or_none(opts.audioquality)) is None: # int_or_none prevents inf, nan
|
audioquality = int_or_none(float_or_none(opts.audioquality)) # int_or_none prevents inf, nan
|
||||||
|
if audioquality is None or audioquality < 0:
|
||||||
parser.error('invalid audio quality specified')
|
parser.error('invalid audio quality specified')
|
||||||
if opts.recodevideo is not None:
|
if opts.recodevideo is not None:
|
||||||
opts.recodevideo = opts.recodevideo.replace(' ', '')
|
opts.recodevideo = opts.recodevideo.replace(' ', '')
|
||||||
|
|
|
@ -16,7 +16,8 @@ from ..utils import (
|
||||||
encodeArgument,
|
encodeArgument,
|
||||||
encodeFilename,
|
encodeFilename,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
get_exe_version,
|
_get_exe_version_output,
|
||||||
|
detect_exe_version,
|
||||||
is_outdated_version,
|
is_outdated_version,
|
||||||
ISO639Utils,
|
ISO639Utils,
|
||||||
orderedSet,
|
orderedSet,
|
||||||
|
@ -80,10 +81,10 @@ class FFmpegPostProcessor(PostProcessor):
|
||||||
|
|
||||||
def _determine_executables(self):
|
def _determine_executables(self):
|
||||||
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
|
programs = ['avprobe', 'avconv', 'ffmpeg', 'ffprobe']
|
||||||
prefer_ffmpeg = True
|
|
||||||
|
|
||||||
def get_ffmpeg_version(path):
|
def get_ffmpeg_version(path, prog):
|
||||||
ver = get_exe_version(path, args=['-version'])
|
out = _get_exe_version_output(path, ['-bsfs'])
|
||||||
|
ver = detect_exe_version(out) if out else False
|
||||||
if ver:
|
if ver:
|
||||||
regexs = [
|
regexs = [
|
||||||
r'(?:\d+:)?([0-9.]+)-[0-9]+ubuntu[0-9.]+$', # Ubuntu, see [1]
|
r'(?:\d+:)?([0-9.]+)-[0-9]+ubuntu[0-9.]+$', # Ubuntu, see [1]
|
||||||
|
@ -94,42 +95,46 @@ class FFmpegPostProcessor(PostProcessor):
|
||||||
mobj = re.match(regex, ver)
|
mobj = re.match(regex, ver)
|
||||||
if mobj:
|
if mobj:
|
||||||
ver = mobj.group(1)
|
ver = mobj.group(1)
|
||||||
return ver
|
self._versions[prog] = ver
|
||||||
|
if prog != 'ffmpeg' or not out:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO: Feature detection
|
||||||
|
|
||||||
self.basename = None
|
self.basename = None
|
||||||
self.probe_basename = None
|
self.probe_basename = None
|
||||||
|
|
||||||
self._paths = None
|
self._paths = None
|
||||||
self._versions = None
|
self._versions = None
|
||||||
if self._downloader:
|
self._features = {}
|
||||||
prefer_ffmpeg = self.get_param('prefer_ffmpeg', True)
|
|
||||||
location = self.get_param('ffmpeg_location')
|
|
||||||
if location is not None:
|
|
||||||
if not os.path.exists(location):
|
|
||||||
self.report_warning(
|
|
||||||
'ffmpeg-location %s does not exist! '
|
|
||||||
'Continuing without ffmpeg.' % (location))
|
|
||||||
self._versions = {}
|
|
||||||
return
|
|
||||||
elif os.path.isdir(location):
|
|
||||||
dirname, basename = location, None
|
|
||||||
else:
|
|
||||||
basename = os.path.splitext(os.path.basename(location))[0]
|
|
||||||
basename = next((p for p in programs if basename.startswith(p)), 'ffmpeg')
|
|
||||||
dirname = os.path.dirname(os.path.abspath(location))
|
|
||||||
if basename in ('ffmpeg', 'ffprobe'):
|
|
||||||
prefer_ffmpeg = True
|
|
||||||
|
|
||||||
self._paths = dict(
|
prefer_ffmpeg = self.get_param('prefer_ffmpeg', True)
|
||||||
(p, os.path.join(dirname, p)) for p in programs)
|
location = self.get_param('ffmpeg_location')
|
||||||
if basename:
|
if location is None:
|
||||||
self._paths[basename] = location
|
self._paths = {p: p for p in programs}
|
||||||
self._versions = dict(
|
else:
|
||||||
(p, get_ffmpeg_version(self._paths[p])) for p in programs)
|
if not os.path.exists(location):
|
||||||
if self._versions is None:
|
self.report_warning(
|
||||||
self._versions = dict(
|
'ffmpeg-location %s does not exist! '
|
||||||
(p, get_ffmpeg_version(p)) for p in programs)
|
'Continuing without ffmpeg.' % (location))
|
||||||
self._paths = dict((p, p) for p in programs)
|
self._versions = {}
|
||||||
|
return
|
||||||
|
elif os.path.isdir(location):
|
||||||
|
dirname, basename = location, None
|
||||||
|
else:
|
||||||
|
basename = os.path.splitext(os.path.basename(location))[0]
|
||||||
|
basename = next((p for p in programs if basename.startswith(p)), 'ffmpeg')
|
||||||
|
dirname = os.path.dirname(os.path.abspath(location))
|
||||||
|
if basename in ('ffmpeg', 'ffprobe'):
|
||||||
|
prefer_ffmpeg = True
|
||||||
|
|
||||||
|
self._paths = dict(
|
||||||
|
(p, os.path.join(dirname, p)) for p in programs)
|
||||||
|
if basename:
|
||||||
|
self._paths[basename] = location
|
||||||
|
|
||||||
|
self._versions = {}
|
||||||
|
for p in programs:
|
||||||
|
get_ffmpeg_version(self._paths[p], p)
|
||||||
|
|
||||||
if prefer_ffmpeg is False:
|
if prefer_ffmpeg is False:
|
||||||
prefs = ('avconv', 'ffmpeg')
|
prefs = ('avconv', 'ffmpeg')
|
||||||
|
@ -382,7 +387,9 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
||||||
|
|
||||||
limits = {
|
limits = {
|
||||||
'libmp3lame': (10, 0),
|
'libmp3lame': (10, 0),
|
||||||
'aac': (0.1, 11),
|
# FFmpeg's AAC encoder does not have an upper limit for the value of -q:a.
|
||||||
|
# Experimentally, with values over 4, bitrate changes were minimal or non-existent
|
||||||
|
'aac': (0.1, 4),
|
||||||
'vorbis': (0, 10),
|
'vorbis': (0, 10),
|
||||||
'opus': None, # doesn't support -q:a
|
'opus': None, # doesn't support -q:a
|
||||||
'wav': None,
|
'wav': None,
|
||||||
|
|
|
@ -4007,10 +4007,7 @@ def check_executable(exe, args=[]):
|
||||||
return exe
|
return exe
|
||||||
|
|
||||||
|
|
||||||
def get_exe_version(exe, args=['--version'],
|
def _get_exe_version_output(exe, args):
|
||||||
version_re=None, unrecognized='present'):
|
|
||||||
""" Returns the version of the specified executable,
|
|
||||||
or False if the executable is not present """
|
|
||||||
try:
|
try:
|
||||||
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
|
# STDIN should be redirected too. On UNIX-like systems, ffmpeg triggers
|
||||||
# SIGTTOU if yt-dlp is run in the background.
|
# SIGTTOU if yt-dlp is run in the background.
|
||||||
|
@ -4022,7 +4019,7 @@ def get_exe_version(exe, args=['--version'],
|
||||||
return False
|
return False
|
||||||
if isinstance(out, bytes): # Python 2.x
|
if isinstance(out, bytes): # Python 2.x
|
||||||
out = out.decode('ascii', 'ignore')
|
out = out.decode('ascii', 'ignore')
|
||||||
return detect_exe_version(out, version_re, unrecognized)
|
return out
|
||||||
|
|
||||||
|
|
||||||
def detect_exe_version(output, version_re=None, unrecognized='present'):
|
def detect_exe_version(output, version_re=None, unrecognized='present'):
|
||||||
|
@ -4036,6 +4033,14 @@ def detect_exe_version(output, version_re=None, unrecognized='present'):
|
||||||
return unrecognized
|
return unrecognized
|
||||||
|
|
||||||
|
|
||||||
|
def get_exe_version(exe, args=['--version'],
|
||||||
|
version_re=None, unrecognized='present'):
|
||||||
|
""" Returns the version of the specified executable,
|
||||||
|
or False if the executable is not present """
|
||||||
|
out = _get_exe_version_output(exe, args)
|
||||||
|
return detect_exe_version(out, version_re, unrecognized) if out else False
|
||||||
|
|
||||||
|
|
||||||
class LazyList(collections.abc.Sequence):
|
class LazyList(collections.abc.Sequence):
|
||||||
''' Lazy immutable list from an iterable
|
''' Lazy immutable list from an iterable
|
||||||
Note that slices of a LazyList are lists and not LazyList'''
|
Note that slices of a LazyList are lists and not LazyList'''
|
||||||
|
|
Loading…
Reference in a new issue