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 __future__ import unicode_literals
|
||||||
from . import nodes
|
from . import nodes
|
||||||
from . import _build
|
from . import _build
|
||||||
|
from . import _detect
|
||||||
from . import _ffmpeg
|
from . import _ffmpeg
|
||||||
from . import _filters
|
from . import _filters
|
||||||
from . import _probe
|
from . import _probe
|
||||||
@ -8,6 +9,7 @@ from . import _run
|
|||||||
from . import _view
|
from . import _view
|
||||||
from .nodes import *
|
from .nodes import *
|
||||||
from ._build import *
|
from ._build import *
|
||||||
|
from ._detect import *
|
||||||
from ._ffmpeg import *
|
from ._ffmpeg import *
|
||||||
from ._filters import *
|
from ._filters import *
|
||||||
from ._probe import *
|
from ._probe import *
|
||||||
@ -17,6 +19,7 @@ from ._view import *
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
nodes.__all__
|
nodes.__all__
|
||||||
+ _build.__all__
|
+ _build.__all__
|
||||||
|
+ _detect.__all__
|
||||||
+ _ffmpeg.__all__
|
+ _ffmpeg.__all__
|
||||||
+ _probe.__all__
|
+ _probe.__all__
|
||||||
+ _run.__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.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
],
|
],
|
||||||
entry_points = {
|
entry_points={
|
||||||
'console_scripts': [
|
'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