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..2dab166ab 100644 --- a/av/codec/codec.pyx +++ b/av/codec/codec.pyx @@ -119,7 +119,8 @@ cdef class Codec: raise RuntimeError("%s is both encoder and decoder.") def __repr__(self): - return f"" + mode = "w" if self.is_encoder else "r" + return f"" def create(self, kind = None): """Create a :class:`.CodecContext` for this codec. @@ -315,16 +316,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 +345,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 +361,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/context.pyi b/av/codec/context.pyi index d8fb07baa..c6a216f78 100644 --- a/av/codec/context.pyi +++ b/av/codec/context.pyi @@ -88,7 +88,9 @@ class CodecContext: def open(self, strict: bool = True) -> None: ... @staticmethod def create( - codec: str | Codec, mode: Literal["r", "w"] | None = None + codec: str | Codec, + mode: Literal["r", "w"] | None = None, + hwaccel: HWAccel | None = None, ) -> CodecContext: ... def parse( self, raw_input: bytes | bytearray | memoryview | None = None diff --git a/av/codec/hwaccel.pyi b/av/codec/hwaccel.pyi index 4078bd73b..5ba8d68bf 100644 --- a/av/codec/hwaccel.pyi +++ b/av/codec/hwaccel.pyi @@ -1,46 +1,48 @@ 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__( self, device_type: str | HWDeviceType, device: str | None = None, - allow_software_fallback: bool = True, - options=None, - **kwargs - ): ... + allow_software_fallback: bool = False, + options: dict[str, object] | None = None, + ) -> None: ... 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..a0c66b05f 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,30 +82,22 @@ 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: - def __init__( - self, - device_type: str | HWDeviceType, - device: str | None = None, - allow_software_fallback: bool = True, - options=None, - **kwargs, - ): + def __init__(self, device_type, device=None, allow_software_fallback=True, options=None): if isinstance(device_type, HWDeviceType): self._device_type = device_type elif isinstance(device_type, str): @@ -115,25 +107,22 @@ cdef class HWAccel: self._device = device self.allow_software_fallback = allow_software_fallback + self.options = {} if not options else dict(options) - if options and kwargs: - raise ValueError("accepts only one of options arg or kwargs") - self.options = dict(options or kwargs) - - def create(self, Codec codec): + def create(self, Codec codec not None): return HWAccelContext( - device_type=HWDeviceType(self._device_type), - device=self._device, - options=self.options, - codec=codec, - allow_software_fallback=self.allow_software_fallback, + HWDeviceType(self._device_type), + self._device, + self.options, + self.allow_software_fallback, + codec, ) cdef class HWAccelContext(HWAccel): - def __init__(self, device_type, device, options, codec, allow_software_fallback, **kwargs): - super().__init__(device_type, device, options, **kwargs) + def __init__(self, device_type, device, allow_software_fallback, options, codec): + super().__init__(device_type, device, options) if not codec: - raise ValueError("codec is required") + raise ValueError("`codec` is required") self.codec = codec cdef HWConfig config @@ -144,7 +133,7 @@ cdef class HWAccelContext(HWAccel): continue break else: - raise NotImplementedError(f"no supported hardware config for {codec}") + raise NotImplementedError(f"No supported hardware config for {codec}") self.config = config cdef char *c_device = NULL @@ -162,6 +151,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/av/container/core.pyi b/av/container/core.pyi index 69907646d..7c681b18b 100644 --- a/av/container/core.pyi +++ b/av/container/core.pyi @@ -44,7 +44,6 @@ class Container: options: dict[str, str] container_options: dict[str, str] stream_options: list[dict[str, str]] - hwaccel: HWAccel streams: StreamContainer metadata: dict[str, str] open_timeout: Real | None diff --git a/tests/test_decode.py b/tests/test_decode.py index 6331d71a3..fc293d201 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.streams.video[0] assert video_stream.codec_context.is_hwaccel frame_count = 0