diff --git a/av/__main__.py b/av/__main__.py index 8b527275d..9e2b9d0ac 100644 --- a/av/__main__.py +++ b/av/__main__.py @@ -33,9 +33,11 @@ def main() -> None: print(f"{libname:<13} {version[0]:3d}.{version[1]:3d}.{version[2]:3d}") if args.hwdevices: - from av.codec.hwaccel import dump_hwdevices + from av.codec.hwaccel import hwdevices_available - dump_hwdevices() + print("Hardware device types:") + for x in hwdevices_available(): + print(" ", x) if args.hwconfigs: from av.codec.codec import dump_hwconfigs diff --git a/av/codec/codec.pyx b/av/codec/codec.pyx index 02db7a8c5..1dda0e6de 100644 --- a/av/codec/codec.pyx +++ b/av/codec/codec.pyx @@ -315,16 +315,18 @@ codec_descriptor = wrap_avclass(lib.avcodec_get_class()) def dump_codecs(): """Print information about available codecs.""" - print('''Codecs: - D.... = Decoding supported - .E... = Encoding supported - ..V.. = Video codec - ..A.. = Audio codec - ..S.. = Subtitle codec - ...I. = Intra frame-only codec - ....L = Lossless compression - .....H = Hardware decoding supported - ------''') + print( + """Codecs: + D..... = Decoding supported + .E.... = Encoding supported + ..V... = Video codec + ..A... = Audio codec + ..S... = Subtitle codec + ...I.. = Intra frame-only codec + ....L. = Lossy compression + .....S = Lossless compression + ------""" + ) for name in sorted(codecs_available): try: @@ -342,14 +344,14 @@ def dump_codecs(): try: print( - " %s%s%s%s%s%s %-18s %s" + " %s%s%s%s%s%s %-18s %s" % ( ".D"[bool(d_codec)], ".E"[bool(e_codec)], codec.type[0].upper(), ".I"[codec.intra_only], - ".L"[codec.lossless], - ".H"[bool((d_codec or codec).hardware_configs)], + ".L"[codec.lossy], + ".S"[codec.lossless], codec.name, codec.long_name, ) @@ -358,15 +360,17 @@ def dump_codecs(): print(f"...... {codec.name:<18} ERROR: {e}") def dump_hwconfigs(): - print('Hardware configs:') + print("Hardware configs:") for name in sorted(codecs_available): try: - codec = Codec(name, 'r') + codec = Codec(name, "r") except ValueError: continue + configs = codec.hardware_configs if not configs: continue - print(' ', codec.name) + + print(" ", codec.name) for config in configs: - print(' ', config) + print(" ", config) diff --git a/av/codec/hwaccel.pyi b/av/codec/hwaccel.pyi index 4078bd73b..8913a4fbe 100644 --- a/av/codec/hwaccel.pyi +++ b/av/codec/hwaccel.pyi @@ -1,34 +1,39 @@ from enum import IntEnum -from typing import Sequence from av.codec.codec import Codec +from av.video.format import VideoFormat class HWDeviceType(IntEnum): - NONE = int - VDPAU = int - CUDA = int - VAAPI = int - DXVA2 = int - QSV = int - VIDEOTOOLBOX = int - D3D11VA = int - DRM = int - OPENCL = int - MEDIACODEC = int - VULKAN = int - D3D12VA = int + none: int + vdpau: int + cuda: int + vaapi: int + dxva2: int + qsv: int + videotoolbox: int + d3d11va: int + drm: int + opencl: int + mediacodec: int + vulkan: int + d3d12va: int -class HWConfig(object): - def __init__(self, sentinel): ... - def __repr__(self): ... +class HWConfigMethod(IntEnum): + none: int + hw_device_ctx: int + hw_frame_ctx: int + internal: int + ad_hoc: int + +class HWConfig: @property - def device_type(self): ... + def device_type(self) -> HWDeviceType: ... @property - def format(self): ... + def format(self) -> VideoFormat: ... @property - def methods(self): ... + def methods(self) -> HWConfigMethod: ... @property - def is_supported(self): ... + def is_supported(self) -> bool: ... class HWAccel: def __init__( @@ -36,11 +41,9 @@ class HWAccel: device_type: str | HWDeviceType, device: str | None = None, allow_software_fallback: bool = True, - options=None, + options: dict[str, object] | None = None, **kwargs ): ... def create(self, codec: Codec): ... -hwdevices_available: Sequence[str] - -def dump_hwdevices() -> None: ... +def hwdevices_available() -> list[str]: ... diff --git a/av/codec/hwaccel.pyx b/av/codec/hwaccel.pyx index d14af33bc..11631627f 100644 --- a/av/codec/hwaccel.pyx +++ b/av/codec/hwaccel.pyx @@ -14,26 +14,26 @@ from av.dictionary import Dictionary class HWDeviceType(IntEnum): - NONE = lib.AV_HWDEVICE_TYPE_NONE - VDPAU = lib.AV_HWDEVICE_TYPE_VDPAU - CUDA = lib.AV_HWDEVICE_TYPE_CUDA - VAAPI = lib.AV_HWDEVICE_TYPE_VAAPI - DXVA2 = lib.AV_HWDEVICE_TYPE_DXVA2 - QSV = lib.AV_HWDEVICE_TYPE_QSV - VIDEOTOOLBOX = lib.AV_HWDEVICE_TYPE_VIDEOTOOLBOX - D3D11VA = lib.AV_HWDEVICE_TYPE_D3D11VA - DRM = lib.AV_HWDEVICE_TYPE_DRM - OPENCL = lib.AV_HWDEVICE_TYPE_OPENCL - MEDIACODEC = lib.AV_HWDEVICE_TYPE_MEDIACODEC - VULKAN = lib.AV_HWDEVICE_TYPE_VULKAN - D3D12VA = lib.AV_HWDEVICE_TYPE_D3D12VA + none = lib.AV_HWDEVICE_TYPE_NONE + vdpau = lib.AV_HWDEVICE_TYPE_VDPAU + cuda = lib.AV_HWDEVICE_TYPE_CUDA + vaapi = lib.AV_HWDEVICE_TYPE_VAAPI + dxva2 = lib.AV_HWDEVICE_TYPE_DXVA2 + qsv = lib.AV_HWDEVICE_TYPE_QSV + videotoolbox = lib.AV_HWDEVICE_TYPE_VIDEOTOOLBOX + d3d11va = lib.AV_HWDEVICE_TYPE_D3D11VA + drm = lib.AV_HWDEVICE_TYPE_DRM + opencl = lib.AV_HWDEVICE_TYPE_OPENCL + mediacodec = lib.AV_HWDEVICE_TYPE_MEDIACODEC + vulkan = lib.AV_HWDEVICE_TYPE_VULKAN + d3d12va = lib.AV_HWDEVICE_TYPE_D3D12VA class HWConfigMethod(IntEnum): - NONE = 0 - HW_DEVICE_CTX = lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX # This is the only one we support. - HW_FRAME_CTX = lib.AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX - INTERNAL = lib.AV_CODEC_HW_CONFIG_METHOD_INTERNAL - AD_HOC = lib.AV_CODEC_HW_CONFIG_METHOD_AD_HOC + none = 0 + hw_device_ctx = lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX # This is the only one we support. + hw_frame_ctx = lib.AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX + internal = lib.AV_CODEC_HW_CONFIG_METHOD_INTERNAL + ad_hoc = lib.AV_CODEC_HW_CONFIG_METHOD_AD_HOC cdef object _cinit_sentinel = object() @@ -82,19 +82,18 @@ cdef class HWConfig: def is_supported(self): return bool(self.ptr.methods & lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) -hwdevices_available = [] -cdef lib.AVHWDeviceType x = lib.AV_HWDEVICE_TYPE_NONE -while True: - x = lib.av_hwdevice_iterate_types(x) - if x == lib.AV_HWDEVICE_TYPE_NONE: - break - hwdevices_available.append(lib.av_hwdevice_get_type_name(HWDeviceType(x))) +cpdef hwdevices_available(): + result = [] -def dump_hwdevices(): - print("Hardware device types:") - for x in hwdevices_available: - print(" ", x) + cdef lib.AVHWDeviceType x = lib.AV_HWDEVICE_TYPE_NONE + while True: + x = lib.av_hwdevice_iterate_types(x) + if x == lib.AV_HWDEVICE_TYPE_NONE: + break + result.append(lib.av_hwdevice_get_type_name(HWDeviceType(x))) + + return result cdef class HWAccel: @@ -162,6 +161,3 @@ cdef class HWAccelContext(HWAccel): def __dealloc__(self): if self.ptr: lib.av_buffer_unref(&self.ptr) - - def create(self, *args, **kwargs): - raise ValueError("cannot call HWAccelContext.create") diff --git a/tests/test_decode.py b/tests/test_decode.py index 6331d71a3..fc3f02714 100644 --- a/tests/test_decode.py +++ b/tests/test_decode.py @@ -204,30 +204,27 @@ def test_side_data(self) -> None: assert frame.rotation == -90 def test_hardware_decode(self) -> None: + hwdevices_available = av.codec.hwaccel.hwdevices_available() if "HWACCEL_DEVICE_TYPE" not in os.environ: pytest.skip( "Set the HWACCEL_DEVICE_TYPE to run this test. " - f"Options are {' '.join(av.codec.hwaccel.hwdevices_available)}" + f"Options are {' '.join(hwdevices_available)}" ) HWACCEL_DEVICE_TYPE = os.environ["HWACCEL_DEVICE_TYPE"] - assert ( - HWACCEL_DEVICE_TYPE in av.codec.hwaccel.hwdevices_available + HWACCEL_DEVICE_TYPE in hwdevices_available ), f"{HWACCEL_DEVICE_TYPE} not available" test_video_path = "tests/assets/black.mp4" make_h264_test_video(test_video_path) - # Test decode. hwaccel = av.codec.hwaccel.HWAccel( device_type=HWACCEL_DEVICE_TYPE, allow_software_fallback=False ) container = av.open(test_video_path, hwaccel=hwaccel) - video_stream = next(s for s in container.streams if s.type == "video") - - assert video_stream is container.streams.video[0] + video_stream = container.stream.video[0] assert video_stream.codec_context.is_hwaccel frame_count = 0