[ffmpeg] Framework for feature detection

Related: #1502, #1237, https://github.com/ytdl-org/youtube-dl/pull/29581
pull/1540/head
pukkandan 3 years ago
parent 31c49255bf
commit 9af98e17bd
No known key found for this signature in database
GPG Key ID: 0F00D95A001F4698

@ -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') prefer_ffmpeg = self.get_param('prefer_ffmpeg', True)
if location is not None: location = self.get_param('ffmpeg_location')
if not os.path.exists(location): if location is None:
self.report_warning( self._paths = {p: p for p in programs}
'ffmpeg-location %s does not exist! ' else:
'Continuing without ffmpeg.' % (location)) if not os.path.exists(location):
self._versions = {} self.report_warning(
return 'ffmpeg-location %s does not exist! '
elif os.path.isdir(location): 'Continuing without ffmpeg.' % (location))
dirname, basename = location, None self._versions = {}
else: return
basename = os.path.splitext(os.path.basename(location))[0] elif os.path.isdir(location):
basename = next((p for p in programs if basename.startswith(p)), 'ffmpeg') dirname, basename = location, None
dirname = os.path.dirname(os.path.abspath(location)) else:
if basename in ('ffmpeg', 'ffprobe'): basename = os.path.splitext(os.path.basename(location))[0]
prefer_ffmpeg = True basename = next((p for p in programs if basename.startswith(p)), 'ffmpeg')
dirname = os.path.dirname(os.path.abspath(location))
self._paths = dict( if basename in ('ffmpeg', 'ffprobe'):
(p, os.path.join(dirname, p)) for p in programs) prefer_ffmpeg = True
if basename:
self._paths[basename] = location self._paths = dict(
self._versions = dict( (p, os.path.join(dirname, p)) for p in programs)
(p, get_ffmpeg_version(self._paths[p])) for p in programs) if basename:
if self._versions is None: self._paths[basename] = location
self._versions = dict(
(p, get_ffmpeg_version(p)) for p in programs) self._versions = {}
self._paths = dict((p, p) for p in programs) 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…
Cancel
Save