From 130457ffe1fb24f70caa25e4ecd580656992d7a8 Mon Sep 17 00:00:00 2001 From: Ross Patterson Date: Fri, 9 Aug 2019 15:10:29 -0700 Subject: [PATCH] Build: Include coders specific to hwaccell APIs --- ffmpeg/_build.py | 41 +++++++++++++++++++++++++++++++++---- ffmpeg/_detect.py | 23 ++++++++++----------- ffmpeg/tests/test_ffmpeg.py | 18 +++++++++++++--- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/ffmpeg/_build.py b/ffmpeg/_build.py index f76939d..d75d17f 100644 --- a/ffmpeg/_build.py +++ b/ffmpeg/_build.py @@ -40,6 +40,8 @@ CODEC_DESCRIPTION_RE = re.compile( CODEC_CODERS_RE = re.compile( r' \((?P(de|en)coders): (?P[^)]+) \)') +HWACCEL_SYNONYMS = dict(cuvid=['nvenc', 'nvdec', 'cuda']) + FILTER_RE = re.compile( r'^ (?P[T.])(?P[S.])(?P[C.]) ' r'(?P[^ ]+) +(?P[^ ]+) +(?P.+)$', @@ -248,22 +250,53 @@ def get_hw_devices(cmd='ffmpeg'): def get_hwaccels(cmd='ffmpeg'): """ - Extract the hwaccels of the ffmpeg build. + Extract the hwaccels of the ffmpeg build, including specific codecs. + + Return all the hardware acceleration APIs supported by this build + including all the codecs that are specific to the API. """ + data = dict(codecs=get_codecs(cmd=cmd), hwaccels=[]) + stdout = _run([cmd, '-hwaccels']) - return stdout.split('\n')[1:-2] + hwaccel_names = stdout.split('\n')[1:-2] + + for hwaccel_name in hwaccel_names: + hwaccel = dict(name=hwaccel_name) + data['hwaccels'].append(hwaccel) + hwaccel['codecs'] = hwaccel_codecs = {} + for codec_name, codec in data['codecs'].items(): + hwaccel_codec = {} + for coders_key in ('decoders', 'encoders'): + matching_coders = [] + for coder in codec.get(coders_key, []): + for synonym in ( + [hwaccel_name] + + HWACCEL_SYNONYMS.get(hwaccel_name, [])): + if ( + coder == synonym or + '_' + synonym in coder or + synonym + '_' in coder): + matching_coders.append(coder) + break + if matching_coders: + hwaccel_codec[coders_key] = matching_coders + if hwaccel_codec: + hwaccel_codecs[codec_name] = hwaccel_codec + + return data def get_build_data(cmd='ffmpeg'): """ Extract details about the ffmpeg build. """ + hwaccels_data = get_hwaccels(cmd=cmd) return dict( version=get_version(cmd=cmd), formats=get_formats(cmd=cmd), demuxers=get_demuxers(cmd=cmd), muxers=get_muxers(cmd=cmd), - codecs=get_codecs(cmd=cmd), + codecs=hwaccels_data['codecs'], bsfs=get_bsfs(cmd=cmd), protocols=get_protocols(cmd=cmd), filters=get_filters(cmd=cmd), @@ -273,7 +306,7 @@ def get_build_data(cmd='ffmpeg'): colors=get_colors(cmd=cmd), devices=get_devices(cmd=cmd), hw_devices=get_hw_devices(cmd=cmd), - hwaccels=get_hwaccels(cmd=cmd)) + hwaccels=hwaccels_data['hwaccels']) __all__ = [ 'get_build_data', diff --git a/ffmpeg/_detect.py b/ffmpeg/_detect.py index 56faee7..c4f0367 100644 --- a/ffmpeg/_detect.py +++ b/ffmpeg/_detect.py @@ -43,10 +43,6 @@ HWACCELS_BY_PERFORMANCE = [ 'qsv', 'd3d11va', 'dxva2', 'vaapi', 'drm'] # Loaded from JSON DATA = None -# Some accelerated codecs use a different prefix than the base codec -CODEC_SYNONYMS = { - 'mpeg1video': 'mpeg1', - 'mpeg2video': 'mpeg2'} def detect_gpu(): @@ -67,14 +63,15 @@ def detect_hwaccels(hwaccels=None, cmd='ffmpeg'): Extract details about the ffmpeg build. """ # Filter against what's available in the ffmpeg build - build_hwaccels = ffmpeg.get_hwaccels(cmd=cmd) + hwaccels_data = ffmpeg.get_hwaccels(cmd=cmd) if hwaccels is None: # Consider all the available hwaccels - hwaccels = build_hwaccels + hwaccels = hwaccels_data['hwaccels'] else: # Support passing in a restricted set of hwaccels hwaccels = [ - hwaccel for hwaccel in hwaccels if hwaccel in build_hwaccels] + hwaccel for hwaccel in hwaccels_data['hwaccels'] + if hwaccel['name'] in hwaccels] # Filter against which APIs are available on this OS+GPU data = _get_data() @@ -82,16 +79,18 @@ def detect_hwaccels(hwaccels=None, cmd='ffmpeg'): gpu = detect_gpu() api_avail = data['hwaccels']['api_avail'][plat_sys][ gpu['vendor'].replace(' Corporation', '')] - hwaccels = [hwaccel for hwaccel in hwaccels if hwaccel in api_avail] + hwaccels = [ + hwaccel for hwaccel in hwaccels if hwaccel['name'] in api_avail] hwaccels.sort(key=lambda hwaccel: ( # Sort unranked hwaccels last, but in the order given by ffmpeg - hwaccel not in HWACCELS_BY_PERFORMANCE, + hwaccel['name'] in HWACCELS_BY_PERFORMANCE and 1 or 0, ( # Sort ranked hwaccels per the constant - hwaccel in HWACCELS_BY_PERFORMANCE and - HWACCELS_BY_PERFORMANCE.index(hwaccel)))) - return hwaccels + hwaccel['name'] in HWACCELS_BY_PERFORMANCE and + HWACCELS_BY_PERFORMANCE.index(hwaccel['name'])))) + hwaccels_data['hwaccels'] = hwaccels + return hwaccels_data def detect_coder( diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 0780eac..7f03af8 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -727,7 +727,16 @@ def test__build_data(): assert isinstance(data['version'], str) - for fields_key in {'formats', 'demuxers', 'muxers', 'codecs', 'filters'}: + assert isinstance(data['codecs'], dict) + for codec, coders in data['codecs'].items(): + assert isinstance(codec, str) + assert isinstance(coders, dict) + assert isinstance(data['hwaccels'], list) + for hwaccel in data['hwaccels']: + assert isinstance(hwaccel, dict) + assert 'name' in hwaccel + + for fields_key in {'formats', 'demuxers', 'muxers', 'filters'}: assert isinstance(data[fields_key], dict) list_keys = {'bsfs'} @@ -741,10 +750,13 @@ def test__build_data(): def test__detect(): - for hwaccels in [ + for hwaccels_data in [ ffmpeg.detect_hwaccels(), ffmpeg.detect_hwaccels(['foohwaccel'])]: - assert isinstance(hwaccels, list) + assert isinstance(hwaccels_data['hwaccels'], list) + for hwaccel in hwaccels_data['hwaccels']: + assert isinstance(hwaccel, dict) + assert 'name' in hwaccel for codecs in [ ffmpeg.detect_codecs('h264', 'h264'),