diff --git a/av/codec/codec.pyi b/av/codec/codec.pyi index 6cb421430..fe2d2fd41 100644 --- a/av/codec/codec.pyi +++ b/av/codec/codec.pyi @@ -1,5 +1,6 @@ +from enum import Flag from fractions import Fraction -from typing import Literal, overload +from typing import ClassVar, Literal, overload from av.audio.codeccontext import AudioCodecContext from av.audio.format import AudioFormat @@ -11,14 +12,14 @@ from av.video.format import VideoFormat from .context import CodecContext -class Properties(EnumFlag): - NONE: int - INTRA_ONLY: int - LOSSY: int - LOSSLESS: int - REORDER: int - BITMAP_SUB: int - TEXT_SUB: int +class Properties(Flag): + NONE: ClassVar[Properties] + INTRA_ONLY: ClassVar[Properties] + LOSSY: ClassVar[Properties] + LOSSLESS: ClassVar[Properties] + REORDER: ClassVar[Properties] + BITMAP_SUB: ClassVar[Properties] + TEXT_SUB: ClassVar[Properties] class Capabilities(EnumFlag): NONE: int @@ -46,25 +47,40 @@ class Capabilities(EnumFlag): class UnknownCodecError(ValueError): ... class Codec: - is_encoder: bool - is_decoder: bool + @property + def is_encoder(self) -> bool: ... + @property + def is_decoder(self) -> bool: ... descriptor: Descriptor - name: str - long_name: str - type: Literal["video", "audio", "data", "subtitle", "attachment"] - id: int + @property + def name(self) -> str: ... + @property + def long_name(self) -> str: ... + @property + def type(self) -> Literal["video", "audio", "data", "subtitle", "attachment"]: ... + @property + def id(self) -> int: ... frame_rates: list[Fraction] | None audio_rates: list[int] | None video_formats: list[VideoFormat] | None audio_formats: list[AudioFormat] | None - properties: Properties + + @property + def properties(self) -> int: ... + @property + def intra_only(self) -> bool: ... + @property + def lossy(self) -> bool: ... + @property + def lossless(self) -> bool: ... + @property + def reorder(self) -> bool: ... + @property + def bitmap_sub(self) -> bool: ... + @property + def text_sub(self) -> bool: ... + capabilities: Capabilities - intra_only: bool - lossy: bool - lossless: bool - reorder: bool - bitmap_sub: bool - text_sub: bool draw_horiz_band: bool dr1: bool hwaccel: bool diff --git a/av/codec/codec.pyx b/av/codec/codec.pyx index da66d7184..9b29cb178 100644 --- a/av/codec/codec.pyx +++ b/av/codec/codec.pyx @@ -4,9 +4,10 @@ from av.enum cimport define_enum from av.utils cimport avrational_to_fraction from av.video.format cimport get_video_format +from enum import Flag -cdef object _cinit_sentinel = object() +cdef object _cinit_sentinel = object() cdef Codec wrap_codec(const lib.AVCodec *ptr): cdef Codec codec = Codec(_cinit_sentinel) @@ -15,34 +16,14 @@ cdef Codec wrap_codec(const lib.AVCodec *ptr): codec._init() return codec - -Properties = define_enum("Properties", "av.codec", ( - ("NONE", 0), - ("INTRA_ONLY", lib.AV_CODEC_PROP_INTRA_ONLY, - """Codec uses only intra compression. - Video and audio codecs only."""), - ("LOSSY", lib.AV_CODEC_PROP_LOSSY, - """Codec supports lossy compression. Audio and video codecs only. - - Note: A codec may support both lossy and lossless - compression modes."""), - ("LOSSLESS", lib.AV_CODEC_PROP_LOSSLESS, - """Codec supports lossless compression. Audio and video codecs only."""), - ("REORDER", lib.AV_CODEC_PROP_REORDER, - """Codec supports frame reordering. That is, the coded order (the order in which - the encoded packets are output by the encoders / stored / input to the - decoders) may be different from the presentation order of the corresponding - frames. - - For codecs that do not have this property set, PTS and DTS should always be - equal."""), - ("BITMAP_SUB", lib.AV_CODEC_PROP_BITMAP_SUB, - """Subtitle codec is bitmap based - Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field."""), - ("TEXT_SUB", lib.AV_CODEC_PROP_TEXT_SUB, - """Subtitle codec is text based. - Decoded AVSubtitle data can be read from the AVSubtitleRect->ass field."""), -), is_flags=True) +class Properties(Flag): + NONE = 0 + INTRA_ONLY = lib.AV_CODEC_PROP_INTRA_ONLY + LOSSY = lib.AV_CODEC_PROP_LOSSY + LOSSLESS = lib.AV_CODEC_PROP_LOSSLESS + REORDER = lib.AV_CODEC_PROP_REORDER + BITMAP_SUB = lib.AV_CODEC_PROP_BITMAP_SUB + TEXT_SUB = lib.AV_CODEC_PROP_TEXT_SUB Capabilities = define_enum("Capabilities", "av.codec", ( ("NONE", 0), @@ -287,21 +268,33 @@ cdef class Codec: i += 1 return ret - # NOTE: there are some overlaps, which we defer to how `ffmpeg -codecs` - # handles them (by prefering the capablity to the property). - # Also, LOSSLESS and LOSSY don't have to agree. - - @Properties.property + @property def properties(self): - """Flag property of :class:`.Properties`""" return self.desc.props - intra_only = properties.flag_property("INTRA_ONLY") - lossy = properties.flag_property("LOSSY") # Defer to capability. - lossless = properties.flag_property("LOSSLESS") # Defer to capability. - reorder = properties.flag_property("REORDER") - bitmap_sub = properties.flag_property("BITMAP_SUB") - text_sub = properties.flag_property("TEXT_SUB") + @property + def intra_only(self): + return bool(self.desc.props & lib.AV_CODEC_PROP_INTRA_ONLY) + + @property + def lossy(self): + return bool(self.desc.props & lib.AV_CODEC_PROP_LOSSY) + + @property + def lossless(self): + return bool(self.desc.props & lib.AV_CODEC_PROP_LOSSLESS) + + @property + def reorder(self): + return bool(self.desc.props & lib.AV_CODEC_PROP_REORDER) + + @property + def bitmap_sub(self): + return bool(self.desc.props & lib.AV_CODEC_PROP_BITMAP_SUB) + + @property + def text_sub(self): + return bool(self.desc.props & lib.AV_CODEC_PROP_TEXT_SUB) @Capabilities.property def capabilities(self): @@ -324,8 +317,6 @@ cdef class Codec: auto_threads = capabilities.flag_property("AUTO_THREADS") variable_frame_size = capabilities.flag_property("VARIABLE_FRAME_SIZE") avoid_probing = capabilities.flag_property("AVOID_PROBING") - # intra_only = capabilities.flag_property("INTRA_ONLY") # Dupes. - # lossless = capabilities.flag_property("LOSSLESS") # Dupes. hardware = capabilities.flag_property("HARDWARE") hybrid = capabilities.flag_property("HYBRID") encoder_reordered_opaque = capabilities.flag_property("ENCODER_REORDERED_OPAQUE") diff --git a/av/sidedata/sidedata.pyi b/av/sidedata/sidedata.pyi index e814bb222..d165513ab 100644 --- a/av/sidedata/sidedata.pyi +++ b/av/sidedata/sidedata.pyi @@ -1,41 +1,52 @@ from collections.abc import Mapping -from typing import Iterator, Sequence, overload +from enum import Enum +from typing import ClassVar, Iterator, Sequence, overload from av.buffer import Buffer -from av.enum import EnumItem from av.frame import Frame -class Type(EnumItem): - PANSCAN: int - A53_CC: int - STEREO3D: int - MATRIXENCODING: int - DOWNMIX_INFO: int - REPLAYGAIN: int - DISPLAYMATRIX: int - AFD: int - MOTION_VECTORS: int - SKIP_SAMPLES: int - AUDIO_SERVICE_TYPE: int - MASTERING_DISPLAY_METADATA: int - GOP_TIMECODE: int - SPHERICAL: int - CONTENT_LIGHT_LEVEL: int - ICC_PROFILE: int - SEI_UNREGISTERED: int - S12M_TIMECODE: int +class Type(Enum): + PANSCAN: ClassVar[Type] + A53_CC: ClassVar[Type] + STEREO3D: ClassVar[Type] + MATRIXENCODING: ClassVar[Type] + DOWNMIX_INFO: ClassVar[Type] + REPLAYGAIN: ClassVar[Type] + DISPLAYMATRIX: ClassVar[Type] + AFD: ClassVar[Type] + MOTION_VECTORS: ClassVar[Type] + SKIP_SAMPLES: ClassVar[Type] + AUDIO_SERVICE_TYPE: ClassVar[Type] + MASTERING_DISPLAY_METADATA: ClassVar[Type] + GOP_TIMECODE: ClassVar[Type] + SPHERICAL: ClassVar[Type] + CONTENT_LIGHT_LEVEL: ClassVar[Type] + ICC_PROFILE: ClassVar[Type] + S12M_TIMECODE: ClassVar[Type] + DYNAMIC_HDR_PLUS: ClassVar[Type] + REGIONS_OF_INTEREST: ClassVar[Type] + VIDEO_ENC_PARAMS: ClassVar[Type] + SEI_UNREGISTERED: ClassVar[Type] + FILM_GRAIN_PARAMS: ClassVar[Type] + DETECTION_BBOXES: ClassVar[Type] + DOVI_RPU_BUFFER: ClassVar[Type] + DOVI_METADATA: ClassVar[Type] + DYNAMIC_HDR_VIVID: ClassVar[Type] + AMBIENT_VIEWING_ENVIRONMENT: ClassVar[Type] + VIDEO_HINT: ClassVar[Type] class SideData(Buffer): type: Type - DISPLAYMATRIX: int class SideDataContainer(Mapping): frame: Frame def __len__(self) -> int: ... def __iter__(self) -> Iterator[SideData]: ... @overload - def __getitem__(self, key: int) -> SideData: ... + def __getitem__(self, key: str | int | Type) -> SideData: ... @overload def __getitem__(self, key: slice) -> Sequence[SideData]: ... @overload - def __getitem__(self, key: int | slice) -> SideData | Sequence[SideData]: ... + def __getitem__( + self, key: str | int | Type | slice + ) -> SideData | Sequence[SideData]: ... diff --git a/av/sidedata/sidedata.pyx b/av/sidedata/sidedata.pyx index 9b423a30b..753496fea 100644 --- a/av/sidedata/sidedata.pyx +++ b/av/sidedata/sidedata.pyx @@ -1,6 +1,5 @@ -from av.enum cimport define_enum - from collections.abc import Mapping +from enum import Enum from av.sidedata.motionvectors import MotionVectors @@ -8,26 +7,41 @@ from av.sidedata.motionvectors import MotionVectors cdef object _cinit_bypass_sentinel = object() -Type = define_enum("Type", __name__, ( - ("PANSCAN", lib.AV_FRAME_DATA_PANSCAN), - ("A53_CC", lib.AV_FRAME_DATA_A53_CC), - ("STEREO3D", lib.AV_FRAME_DATA_STEREO3D), - ("MATRIXENCODING", lib.AV_FRAME_DATA_MATRIXENCODING), - ("DOWNMIX_INFO", lib.AV_FRAME_DATA_DOWNMIX_INFO), - ("REPLAYGAIN", lib.AV_FRAME_DATA_REPLAYGAIN), - ("DISPLAYMATRIX", lib.AV_FRAME_DATA_DISPLAYMATRIX), - ("AFD", lib.AV_FRAME_DATA_AFD), - ("MOTION_VECTORS", lib.AV_FRAME_DATA_MOTION_VECTORS), - ("SKIP_SAMPLES", lib.AV_FRAME_DATA_SKIP_SAMPLES), - ("AUDIO_SERVICE_TYPE", lib.AV_FRAME_DATA_AUDIO_SERVICE_TYPE), - ("MASTERING_DISPLAY_METADATA", lib.AV_FRAME_DATA_MASTERING_DISPLAY_METADATA), - ("GOP_TIMECODE", lib.AV_FRAME_DATA_GOP_TIMECODE), - ("SPHERICAL", lib.AV_FRAME_DATA_SPHERICAL), - ("CONTENT_LIGHT_LEVEL", lib.AV_FRAME_DATA_CONTENT_LIGHT_LEVEL), - ("ICC_PROFILE", lib.AV_FRAME_DATA_ICC_PROFILE), - ("SEI_UNREGISTERED", lib.AV_FRAME_DATA_SEI_UNREGISTERED), - ("S12M_TIMECODE", lib.AV_FRAME_DATA_S12M_TIMECODE), -)) +class Type(Enum): + """ + Enum class representing different types of frame data in audio/video processing. + Values are mapped to corresponding AV_FRAME_DATA constants from FFmpeg. + + From: https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/frame.h + """ + PANSCAN = lib.AV_FRAME_DATA_PANSCAN + A53_CC = lib.AV_FRAME_DATA_A53_CC + STEREO3D = lib.AV_FRAME_DATA_STEREO3D + MATRIXENCODING = lib.AV_FRAME_DATA_MATRIXENCODING + DOWNMIX_INFO = lib.AV_FRAME_DATA_DOWNMIX_INFO + REPLAYGAIN = lib.AV_FRAME_DATA_REPLAYGAIN + DISPLAYMATRIX = lib.AV_FRAME_DATA_DISPLAYMATRIX + AFD = lib.AV_FRAME_DATA_AFD + MOTION_VECTORS = lib.AV_FRAME_DATA_MOTION_VECTORS + SKIP_SAMPLES = lib.AV_FRAME_DATA_SKIP_SAMPLES + AUDIO_SERVICE_TYPE = lib.AV_FRAME_DATA_AUDIO_SERVICE_TYPE + MASTERING_DISPLAY_METADATA = lib.AV_FRAME_DATA_MASTERING_DISPLAY_METADATA + GOP_TIMECODE = lib.AV_FRAME_DATA_GOP_TIMECODE + SPHERICAL = lib.AV_FRAME_DATA_SPHERICAL + CONTENT_LIGHT_LEVEL = lib.AV_FRAME_DATA_CONTENT_LIGHT_LEVEL + ICC_PROFILE = lib.AV_FRAME_DATA_ICC_PROFILE + S12M_TIMECODE = lib.AV_FRAME_DATA_S12M_TIMECODE + DYNAMIC_HDR_PLUS = lib.AV_FRAME_DATA_DYNAMIC_HDR_PLUS + REGIONS_OF_INTEREST = lib.AV_FRAME_DATA_REGIONS_OF_INTEREST + VIDEO_ENC_PARAMS = lib.AV_FRAME_DATA_VIDEO_ENC_PARAMS + SEI_UNREGISTERED = lib.AV_FRAME_DATA_SEI_UNREGISTERED + FILM_GRAIN_PARAMS = lib.AV_FRAME_DATA_FILM_GRAIN_PARAMS + DETECTION_BBOXES = lib.AV_FRAME_DATA_DETECTION_BBOXES + DOVI_RPU_BUFFER = lib.AV_FRAME_DATA_DOVI_RPU_BUFFER + DOVI_METADATA = lib.AV_FRAME_DATA_DOVI_METADATA + DYNAMIC_HDR_VIVID = lib.AV_FRAME_DATA_DYNAMIC_HDR_VIVID + AMBIENT_VIEWING_ENVIRONMENT = lib.AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT + VIDEO_HINT = lib.AV_FRAME_DATA_VIDEO_HINT cdef SideData wrap_side_data(Frame frame, int index): @@ -60,7 +74,7 @@ cdef class SideData(Buffer): @property def type(self): - return Type.get(self.ptr.type) or self.ptr.type + return Type(self.ptr.type) cdef class _SideDataContainer: @@ -85,9 +99,9 @@ cdef class _SideDataContainer: def __getitem__(self, key): if isinstance(key, int): return self._by_index[key] - - type_ = Type.get(key) - return self._by_type[type_] + if isinstance(key, str): + return self._by_type[Type[key]] + return self._by_type[key] class SideDataContainer(_SideDataContainer, Mapping): diff --git a/av/stream.pyi b/av/stream.pyi index 8c48f3ac7..b17587e10 100644 --- a/av/stream.pyi +++ b/av/stream.pyi @@ -1,26 +1,24 @@ +from enum import Enum from fractions import Fraction -from typing import Literal +from typing import ClassVar, Literal from .codec import Codec, CodecContext from .container import Container -from .enum import EnumItem from .frame import Frame from .packet import Packet -class SideData(EnumItem): - DISPLAYMATRIX: int +class SideData(Enum): + DISPLAYMATRIX: ClassVar[SideData] class Stream: name: str | None - thread_type: Literal["NONE", "FRAME", "SLICE", "AUTO"] - container: Container codec: Codec codec_context: CodecContext metadata: dict[str, str] id: int profiles: list[str] - profile: str + profile: str | None index: int time_base: Fraction | None average_rate: Fraction | None diff --git a/av/stream.pyx b/av/stream.pyx index 4c450d283..59e1713c5 100644 --- a/av/stream.pyx +++ b/av/stream.pyx @@ -1,7 +1,8 @@ cimport libav as lib from libc.stdint cimport int32_t -from av.enum cimport define_enum +from enum import Enum + from av.error cimport err_check from av.packet cimport Packet from av.utils cimport ( @@ -14,12 +15,10 @@ from av.utils cimport ( cdef object _cinit_bypass_sentinel = object() - # If necessary more can be added from # https://ffmpeg.org/doxygen/trunk/group__lavc__packet.html#ga9a80bfcacc586b483a973272800edb97 -SideData = define_enum("SideData", __name__, ( - ("DISPLAYMATRIX", lib.AV_PKT_DATA_DISPLAYMATRIX, "Display Matrix"), -)) +class SideData(Enum): + DISPLAYMATRIX: "Display Matrix" = lib.AV_PKT_DATA_DISPLAYMATRIX cdef Stream wrap_stream(Container container, lib.AVStream *c_stream, CodecContext codec_context): """Build an av.Stream for an existing AVStream. diff --git a/av/video/frame.pyi b/av/video/frame.pyi index 3300e8607..d837ed606 100644 --- a/av/video/frame.pyi +++ b/av/video/frame.pyi @@ -1,9 +1,9 @@ -from typing import Any, Union +from enum import IntEnum +from typing import Any, ClassVar, Union import numpy as np from PIL import Image -from av.enum import EnumItem from av.frame import Frame from .format import VideoFormat @@ -15,7 +15,7 @@ _SupportedNDarray = Union[ np.ndarray[Any, np.dtype[np.float32]], ] -class PictureType(EnumItem): +class PictureType(IntEnum): NONE: int I: int P: int @@ -28,15 +28,19 @@ class PictureType(EnumItem): class VideoFrame(Frame): format: VideoFormat pts: int - time: float planes: tuple[VideoPlane, ...] - width: int - height: int - interlaced_frame: bool pict_type: int colorspace: int color_range: int + @property + def time(self) -> float: ... + @property + def width(self) -> int: ... + @property + def height(self) -> int: ... + @property + def interlaced_frame(self) -> bool: ... def __init__( self, width: int = 0, height: int = 0, format: str = "yuv420p" ) -> None: ... diff --git a/av/video/frame.pyx b/av/video/frame.pyx index e5cd523b5..80cf266f8 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -1,8 +1,8 @@ import sys +from enum import IntEnum from libc.stdint cimport uint8_t -from av.enum cimport define_enum from av.error cimport err_check from av.utils cimport check_ndarray from av.video.format cimport get_pix_fmt, get_video_format @@ -20,18 +20,15 @@ cdef VideoFrame alloc_video_frame(): """ return VideoFrame.__new__(VideoFrame, _cinit_bypass_sentinel) - -PictureType = define_enum("PictureType", __name__, ( - ("NONE", lib.AV_PICTURE_TYPE_NONE, "Undefined"), - ("I", lib.AV_PICTURE_TYPE_I, "Intra"), - ("P", lib.AV_PICTURE_TYPE_P, "Predicted"), - ("B", lib.AV_PICTURE_TYPE_B, "Bi-directional predicted"), - ("S", lib.AV_PICTURE_TYPE_S, "S(GMC)-VOP MPEG-4"), - ("SI", lib.AV_PICTURE_TYPE_SI, "Switching intra"), - ("SP", lib.AV_PICTURE_TYPE_SP, "Switching predicted"), - ("BI", lib.AV_PICTURE_TYPE_BI, "BI type"), -)) - +class PictureType(IntEnum): + NONE = lib.AV_PICTURE_TYPE_NONE # Undefined + I = lib.AV_PICTURE_TYPE_I # Intra + P = lib.AV_PICTURE_TYPE_P # Predicted + B = lib.AV_PICTURE_TYPE_B # Bi-directional predicted + S = lib.AV_PICTURE_TYPE_S # S(GMC)-VOP MPEG-4 + SI = lib.AV_PICTURE_TYPE_SI # Switching intra + SP = lib.AV_PICTURE_TYPE_SP # Switching predicted + BI = lib.AV_PICTURE_TYPE_BI # BI type cdef byteswap_array(array, bint big_endian): if (sys.byteorder == "big") != big_endian: @@ -183,16 +180,17 @@ cdef class VideoFrame(Frame): @property def pict_type(self): - """One of :class:`.PictureType`. + """Returns an integer that corresponds to the PictureType enum. - Wraps :ffmpeg:`AVFrame.pict_type`. + Wraps :ffmpeg:`AVFrame.pict_type` + :type: int """ - return PictureType.get(self.ptr.pict_type, create=True) + return self.ptr.pict_type @pict_type.setter def pict_type(self, value): - self.ptr.pict_type = PictureType[value].value + self.ptr.pict_type = value @property def colorspace(self): diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd index 54fb2293c..172c9cc65 100644 --- a/include/libavcodec/avcodec.pxd +++ b/include/libavcodec/avcodec.pxd @@ -354,10 +354,18 @@ cdef extern from "libavcodec/avcodec.h" nogil: AV_FRAME_DATA_SPHERICAL AV_FRAME_DATA_CONTENT_LIGHT_LEVEL AV_FRAME_DATA_ICC_PROFILE - AV_FRAME_DATA_QP_TABLE_PROPERTIES - AV_FRAME_DATA_QP_TABLE_DATA - AV_FRAME_DATA_SEI_UNREGISTERED AV_FRAME_DATA_S12M_TIMECODE + AV_FRAME_DATA_DYNAMIC_HDR_PLUS + AV_FRAME_DATA_REGIONS_OF_INTEREST + AV_FRAME_DATA_VIDEO_ENC_PARAMS + AV_FRAME_DATA_SEI_UNREGISTERED + AV_FRAME_DATA_FILM_GRAIN_PARAMS + AV_FRAME_DATA_DETECTION_BBOXES + AV_FRAME_DATA_DOVI_RPU_BUFFER + AV_FRAME_DATA_DOVI_METADATA + AV_FRAME_DATA_DYNAMIC_HDR_VIVID + AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT + AV_FRAME_DATA_VIDEO_HINT cdef struct AVFrameSideData: AVFrameSideDataType type diff --git a/tests/test_file_probing.py b/tests/test_file_probing.py index f71391697..ce04189f9 100644 --- a/tests/test_file_probing.py +++ b/tests/test_file_probing.py @@ -75,6 +75,7 @@ def test_container_probing(self) -> None: def test_stream_probing(self) -> None: stream = self.file.streams[0] + assert isinstance(stream, av.AudioStream) assert str(stream).startswith( " None: def test_stream_probing(self) -> None: stream = self.file.streams[0] + assert isinstance(stream, av.VideoStream) assert str(stream).startswith("