mirror of
https://github.com/kkroening/ffmpeg-python.git
synced 2025-08-07 18:39:47 +08:00
Detect: Basic hwaccel detection based on OS and GPU, Linux only ATM
This commit is contained in:
parent
891ca521fd
commit
f17766aec3
19
Makefile
Normal file
19
Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
## Automate common development tasks
|
||||
|
||||
|
||||
.PHONY: default
|
||||
defalt: ffmpeg/detect.json
|
||||
|
||||
|
||||
.tox/py35/bin/python:
|
||||
tox -e py35
|
||||
touch "$(@)"
|
||||
|
||||
.tox/py35/lib/python3.5/site-packages/pandas: .tox/py35/bin/python
|
||||
.tox/py35/bin/pip install requests lxml pandas
|
||||
touch "$(@)"
|
||||
|
||||
.PHONY: ffmpeg/detect.json
|
||||
ffmpeg/detect.json: .tox/py35/lib/python3.5/site-packages/pandas
|
||||
.tox/py35/bin/python examples/get_detect_data.py >"$(@)"
|
||||
|
59
examples/get_detect_data.py
Executable file
59
examples/get_detect_data.py
Executable file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Retrieve and process all the external data for hardware detection.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
||||
import requests
|
||||
import pandas
|
||||
|
||||
HWACCELINTRO_URL = 'https://trac.ffmpeg.org/wiki/HWAccelIntro'
|
||||
API_TO_HWACCEL = {
|
||||
'AMF': 'amf',
|
||||
'NVENC/NVDEC/CUVID': 'cuvid',
|
||||
'Direct3D 11': 'd3d11va',
|
||||
'Direct3D 9 (DXVA2)': 'dxva2',
|
||||
'libmfx': 'libmfx',
|
||||
'MediaCodec': 'mediacodec',
|
||||
'Media Foundation': 'mediafoundation',
|
||||
'MMAL': 'mmal',
|
||||
'OpenCL': 'opencl',
|
||||
'OpenMAX': 'omx',
|
||||
'V4L2 M2M': 'v4l2m2m',
|
||||
'VAAPI': 'vaapi',
|
||||
'VDPAU': 'vdpau',
|
||||
'VideoToolbox': 'videotoolbox',
|
||||
}
|
||||
PLATFORM_TO_PY = {
|
||||
'Apple': 'Darwin',
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
data = {}
|
||||
|
||||
data['hwaccels'] = hwaccels = {}
|
||||
response = requests.get(HWACCELINTRO_URL)
|
||||
api_avail_table, impl_table = pandas.read_html(response.content)
|
||||
|
||||
gpu_vendor_cols = api_avail_table.loc[1][1:]
|
||||
platform_cols = api_avail_table.loc[0][1:]
|
||||
api_rows = api_avail_table[0][2:]
|
||||
|
||||
hwaccels['api_avail'] = platforms = {}
|
||||
for gpu_vendor_idx, gpu_vendor in enumerate(gpu_vendor_cols):
|
||||
platform = platform_cols[gpu_vendor_idx + 1]
|
||||
platform = PLATFORM_TO_PY.get(platform, platform)
|
||||
gpu_vendors = platforms.setdefault(platform, {})
|
||||
avail_hwaccels = gpu_vendors.setdefault(gpu_vendor, [])
|
||||
for api_idx, api in enumerate(api_rows):
|
||||
if api_avail_table[gpu_vendor_idx + 1][api_idx + 2] != 'N':
|
||||
avail_hwaccels.append(API_TO_HWACCEL[api])
|
||||
|
||||
json.dump(data, sys.stdout, indent=2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
from . import nodes
|
||||
from . import _build
|
||||
from . import _detect
|
||||
from . import _ffmpeg
|
||||
from . import _filters
|
||||
from . import _probe
|
||||
@ -8,6 +9,7 @@ from . import _run
|
||||
from . import _view
|
||||
from .nodes import *
|
||||
from ._build import *
|
||||
from ._detect import *
|
||||
from ._ffmpeg import *
|
||||
from ._filters import *
|
||||
from ._probe import *
|
||||
@ -17,6 +19,7 @@ from ._view import *
|
||||
__all__ = (
|
||||
nodes.__all__
|
||||
+ _build.__all__
|
||||
+ _detect.__all__
|
||||
+ _ffmpeg.__all__
|
||||
+ _probe.__all__
|
||||
+ _run.__all__
|
||||
|
121
ffmpeg/_detect.py
Normal file
121
ffmpeg/_detect.py
Normal file
@ -0,0 +1,121 @@
|
||||
"""Detect optimal arguments for various options.
|
||||
|
||||
This module includes a number of constants used to attempt to detect the
|
||||
options which will provide the best performance for a given OS/GPU/etc..
|
||||
|
||||
For most of these constants, it only matters that the best performing option
|
||||
available for a given OS/platform/hardware rank first for that
|
||||
OS/platform/hardware, not which OS/platform/hardware performs better. For
|
||||
example, it doesn't matter if `vdpau` is lower than `cuvid` or vice versa,
|
||||
because one is only available for Linux and the other for Windows. Similarly,
|
||||
it doesn't matter how `amf` is ranked with respect to `cuvid` because one is
|
||||
only available on NVidia GPUs and the other AMD. It *does* matter how
|
||||
`cuvid`/`amf` are ranked with respect to `dxva2` because those could both be
|
||||
available on the same OS and GPU.
|
||||
|
||||
Additions and suggestions for these constants are very much welcome,
|
||||
especially if they come with benchmarks and/or good explanations from those
|
||||
who understand this domain well. Contributions of more complicated or
|
||||
involved detection logic may also be welcome, though the case will have to be
|
||||
made more rigorously.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import platform
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
import ffmpeg
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
'--ffmpeg', default='ffmpeg',
|
||||
help='The path to the ffmpeg execuatble')
|
||||
|
||||
# List `hwaccel` options by order of expected performance when available.
|
||||
HWACCELS_BY_PERFORMANCE = [
|
||||
'cuvid', 'amf', 'vdpau',
|
||||
'qsv', 'd3d11va', 'dxva2', 'vaapi', 'drm']
|
||||
# Loaded from JSON
|
||||
DATA = None
|
||||
|
||||
|
||||
def detect_gpu():
|
||||
"""
|
||||
Detect the GPU vendor, generation and model if possible.
|
||||
"""
|
||||
plat_sys = platform.system()
|
||||
if plat_sys == 'Linux':
|
||||
display_output = subprocess.check_output(
|
||||
['lshw', '-class', 'display', '-json'])
|
||||
return json.loads(display_output.decode().strip().strip(','))
|
||||
|
||||
|
||||
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)
|
||||
if hwaccels is None:
|
||||
# Consider all the available hwaccels
|
||||
hwaccels = build_hwaccels
|
||||
else:
|
||||
# Support passing in a restricted set of hwaccels
|
||||
hwaccels = [
|
||||
hwaccel for hwaccel in hwaccels if hwaccel in build_hwaccels]
|
||||
|
||||
# Filter against which APIs are available on this OS+GPU
|
||||
data = _get_data()
|
||||
plat_sys = platform.system()
|
||||
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.sort(key=lambda hwaccel: (
|
||||
# Sort unranked hwaccels last, but in the order given by ffmpeg
|
||||
hwaccel not in HWACCELS_BY_PERFORMANCE,
|
||||
(
|
||||
# Sort ranked hwaccels per the constant
|
||||
hwaccel in HWACCELS_BY_PERFORMANCE and
|
||||
HWACCELS_BY_PERFORMANCE.index(hwaccel))))
|
||||
return hwaccels
|
||||
|
||||
|
||||
__all__ = [
|
||||
'detect_gpu',
|
||||
'detect_hwaccels',
|
||||
]
|
||||
|
||||
|
||||
def _get_data():
|
||||
"""
|
||||
Don't load the data JSON unless needed, cache in a global.
|
||||
"""
|
||||
global DATA
|
||||
if DATA is None:
|
||||
with open(os.path.join(
|
||||
os.path.dirname(__file__), 'detect.json')) as data_opened:
|
||||
DATA = json.load(data_opened)
|
||||
return DATA
|
||||
|
||||
|
||||
def main(args=None):
|
||||
"""
|
||||
Dump all ffmpeg build data to json.
|
||||
"""
|
||||
args = parser.parse_args(args)
|
||||
data = dict(
|
||||
gpu=detect_gpu(),
|
||||
hwaccels=detect_hwaccels(args.ffmpeg))
|
||||
json.dump(data, sys.stdout, indent=2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
71
ffmpeg/detect.json
Normal file
71
ffmpeg/detect.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"hwaccels": {
|
||||
"api_avail": {
|
||||
"Linux": {
|
||||
"AMD": [
|
||||
"opencl",
|
||||
"omx",
|
||||
"vaapi",
|
||||
"vdpau"
|
||||
],
|
||||
"NVIDIA": [
|
||||
"cuvid",
|
||||
"opencl",
|
||||
"vaapi",
|
||||
"vdpau"
|
||||
],
|
||||
"Intel": [
|
||||
"libmfx",
|
||||
"opencl",
|
||||
"vaapi"
|
||||
]
|
||||
},
|
||||
"Darwin": {
|
||||
"iOS": [
|
||||
"videotoolbox"
|
||||
],
|
||||
"macOS": [
|
||||
"opencl",
|
||||
"videotoolbox"
|
||||
]
|
||||
},
|
||||
"Android": {
|
||||
"NaN": [
|
||||
"mediacodec",
|
||||
"opencl",
|
||||
"omx",
|
||||
"v4l2m2m"
|
||||
]
|
||||
},
|
||||
"Other": {
|
||||
"Raspberry Pi": [
|
||||
"mmal",
|
||||
"omx"
|
||||
]
|
||||
},
|
||||
"Windows": {
|
||||
"AMD": [
|
||||
"amf",
|
||||
"d3d11va",
|
||||
"dxva2",
|
||||
"mediafoundation",
|
||||
"opencl"
|
||||
],
|
||||
"NVIDIA": [
|
||||
"cuvid",
|
||||
"d3d11va",
|
||||
"dxva2",
|
||||
"mediafoundation",
|
||||
"opencl"
|
||||
],
|
||||
"Intel": [
|
||||
"d3d11va",
|
||||
"dxva2",
|
||||
"libmfx",
|
||||
"mediafoundation",
|
||||
"opencl"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
setup.py
5
setup.py
@ -95,8 +95,9 @@ setup(
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
],
|
||||
entry_points = {
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'ffmpeg-build-json=ffmpeg._build:main'],
|
||||
'ffmpeg-build-json=ffmpeg._build:main',
|
||||
'ffmpeg-detect=ffmpeg._detect:main'],
|
||||
},
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user