diff --git a/av/buffer.pyx b/av/buffer.py similarity index 60% rename from av/buffer.pyx rename to av/buffer.py index 20c7c4877..047d1a157 100644 --- a/av/buffer.pyx +++ b/av/buffer.py @@ -1,25 +1,27 @@ -from cpython cimport PyBUF_WRITABLE, PyBuffer_FillInfo -from libc.string cimport memcpy +import cython +from cython.cimports.av.bytesource import ByteSource, bytesource +from cython.cimports.cpython import PyBUF_WRITABLE, PyBuffer_FillInfo +from cython.cimports.libc.string import memcpy -from av.bytesource cimport ByteSource, bytesource - -cdef class Buffer: +@cython.cclass +class Buffer: """A base class for PyAV objects which support the buffer protocol, such as :class:`.Packet` and :class:`.Plane`. """ - cdef size_t _buffer_size(self): + @cython.cfunc + def _buffer_size(self) -> cython.size_t: return 0 - cdef void* _buffer_ptr(self): - return NULL + def _buffer_ptr(self) -> cython.p_void: + return cython.NULL - cdef bint _buffer_writable(self): + def _buffer_writable(self) -> cython.bint: return True - def __getbuffer__(self, Py_buffer *view, int flags): + def __getbuffer__(self, view: cython.pointer[Py_buffer], flags: cython.int): if flags & PyBUF_WRITABLE and not self._buffer_writable(): raise ValueError("buffer is not writable") @@ -33,20 +35,20 @@ def buffer_size(self): @property def buffer_ptr(self): """The memory address of the buffer.""" - return self._buffer_ptr() + return cython.cast(cython.size_t, self._buffer_ptr()) def update(self, input): """Replace the data in this object with the given buffer. Accepts anything that supports the `buffer protocol `_, - e.g. bytes, Numpy arrays, other :class:`Buffer` objects, etc.. + e.g. bytes, NumPy arrays, other :class:`Buffer` objects, etc.. """ if not self._buffer_writable(): raise ValueError("buffer is not writable") - cdef ByteSource source = bytesource(input) - cdef size_t size = self._buffer_size() + source: ByteSource = bytesource(input) + size: cython.size_t = self._buffer_size() if source.length != size: raise ValueError(f"got {source.length} bytes; need {size} bytes") diff --git a/av/subtitles/subtitle.py b/av/subtitles/subtitle.py index cdf0e16ec..90eef433f 100644 --- a/av/subtitles/subtitle.py +++ b/av/subtitles/subtitle.py @@ -1,6 +1,5 @@ import cython from cython.cimports.cpython import PyBuffer_FillInfo, PyBytes_FromString -from cython.cimports.libc.stdint import int64_t, uint64_t from cython.cimports.libc.string import memcpy, strlen @@ -10,7 +9,7 @@ def __dealloc__(self): lib.avsubtitle_free(cython.address(self.struct)) -_cinit_bypass_sentinel = object() +_cinit_bypass_sentinel = cython.declare(object, object()) @cython.cclass @@ -293,7 +292,7 @@ def dialogue(self): Extract the dialogue from the ass format. Strip comments. """ comma_count: cython.short = 0 - i: uint64_t = 0 + i: cython.Py_ssize_t = 0 state: cython.bint = False ass_text: bytes = self.ass char, next_char = cython.declare(cython.char) diff --git a/av/video/reformatter.pyx b/av/video/reformatter.py similarity index 66% rename from av/video/reformatter.pyx rename to av/video/reformatter.py index 465df6278..dc898be84 100644 --- a/av/video/reformatter.pyx +++ b/av/video/reformatter.py @@ -1,12 +1,11 @@ -cimport libav as lib -from libc.stdint cimport uint8_t - -from av.error cimport err_check -from av.video.format cimport VideoFormat -from av.video.frame cimport alloc_video_frame - from enum import IntEnum +import cython +import cython.cimports.libav as lib +from cython.cimports.av.error import err_check +from cython.cimports.av.video.format import VideoFormat +from cython.cimports.av.video.frame import alloc_video_frame + class Interpolation(IntEnum): FAST_BILINEAR: "Fast bilinear" = lib.SWS_FAST_BILINEAR @@ -38,6 +37,7 @@ class Colorspace(IntEnum): smpte240m = lib.SWS_CS_SMPTE240M default = lib.SWS_CS_DEFAULT + class ColorRange(IntEnum): UNSPECIFIED: "Unspecified" = lib.AVCOL_RANGE_UNSPECIFIED MPEG: "MPEG (limited) YUV range, 219*2^(n-8)" = lib.AVCOL_RANGE_MPEG @@ -58,7 +58,8 @@ def _resolve_enum_value(value, enum_class, default): raise ValueError(f"Cannot convert {value} to {enum_class.__name__}") -cdef class VideoReformatter: +@cython.cclass +class VideoReformatter: """An object for reformatting size and pixel format of :class:`.VideoFrame`. It is most efficient to have a reformatter object for each set of parameters @@ -67,13 +68,21 @@ def _resolve_enum_value(value, enum_class, default): """ def __dealloc__(self): - with nogil: + with cython.nogil: lib.sws_freeContext(self.ptr) - def reformat(self, VideoFrame frame not None, width=None, height=None, - format=None, src_colorspace=None, dst_colorspace=None, - interpolation=None, src_color_range=None, - dst_color_range=None): + def reformat( + self, + frame: VideoFrame, + width=None, + height=None, + format=None, + src_colorspace=None, + dst_colorspace=None, + interpolation=None, + src_color_range=None, + dst_color_range=None, + ): """Create a new :class:`VideoFrame` with the given width/height/format/colorspace. Returns the same frame untouched if nothing needs to be done to it. @@ -95,13 +104,24 @@ def reformat(self, VideoFrame frame not None, width=None, height=None, """ - cdef VideoFormat video_format = VideoFormat(format if format is not None else frame.format) - - cdef int c_src_colorspace = _resolve_enum_value(src_colorspace, Colorspace, frame.colorspace) - cdef int c_dst_colorspace = _resolve_enum_value(dst_colorspace, Colorspace, frame.colorspace) - cdef int c_interpolation = _resolve_enum_value(interpolation, Interpolation, int(Interpolation.BILINEAR)) - cdef int c_src_color_range = _resolve_enum_value(src_color_range, ColorRange, 0) - cdef int c_dst_color_range = _resolve_enum_value(dst_color_range, ColorRange, 0) + video_format: VideoFormat = VideoFormat( + format if format is not None else frame.format + ) + c_src_colorspace: cython.int = _resolve_enum_value( + src_colorspace, Colorspace, frame.colorspace + ) + c_dst_colorspace: cython.int = _resolve_enum_value( + dst_colorspace, Colorspace, frame.colorspace + ) + c_interpolation: cython.int = _resolve_enum_value( + interpolation, Interpolation, int(Interpolation.BILINEAR) + ) + c_src_color_range: cython.int = _resolve_enum_value( + src_color_range, ColorRange, 0 + ) + c_dst_color_range: cython.int = _resolve_enum_value( + dst_color_range, ColorRange, 0 + ) return self._reformat( frame, @@ -115,11 +135,19 @@ def reformat(self, VideoFrame frame not None, width=None, height=None, c_dst_color_range, ) - cdef _reformat(self, VideoFrame frame, int width, int height, - lib.AVPixelFormat dst_format, int src_colorspace, - int dst_colorspace, int interpolation, - int src_color_range, int dst_color_range): - + @cython.cfunc + def _reformat( + self, + frame: VideoFrame, + width: cython.int, + height: cython.int, + dst_format: lib.AVPixelFormat, + src_colorspace: cython.int, + dst_colorspace: cython.int, + interpolation: cython.int, + src_color_range: cython.int, + dst_color_range: cython.int, + ): if frame.ptr.format < 0: raise ValueError("Frame does not have format set.") @@ -127,19 +155,19 @@ def reformat(self, VideoFrame frame not None, width=None, height=None, src_color_range = 1 if src_color_range == ColorRange.JPEG.value else 0 dst_color_range = 1 if dst_color_range == ColorRange.JPEG.value else 0 - cdef lib.AVPixelFormat src_format = frame.ptr.format + src_format = cython.cast(lib.AVPixelFormat, frame.ptr.format) # Shortcut! if ( - dst_format == src_format and - width == frame.ptr.width and - height == frame.ptr.height and - dst_colorspace == src_colorspace and - src_color_range == dst_color_range + dst_format == src_format + and width == frame.ptr.width + and height == frame.ptr.height + and dst_colorspace == src_colorspace + and src_color_range == dst_color_range ): return frame - with nogil: + with cython.nogil: self.ptr = lib.sws_getCachedContext( self.ptr, frame.ptr.width, @@ -149,36 +177,37 @@ def reformat(self, VideoFrame frame not None, width=None, height=None, height, dst_format, interpolation, - NULL, - NULL, - NULL + cython.NULL, + cython.NULL, + cython.NULL, ) # We want to change the colorspace/color_range transforms. - # We do that by grabbing all of the current settings, changing a + # We do that by grabbing all the current settings, changing a # couple, and setting them all. We need a lot of state here. - cdef int *inv_tbl - cdef int *tbl - cdef int src_colorspace_range, dst_colorspace_range - cdef int brightness, contrast, saturation - cdef int ret + inv_tbl: cython.p_int + tbl: cython.p_int + src_colorspace_range: cython.int + dst_colorspace_range: cython.int + brightness: cython.int + contrast: cython.int + saturation: cython.int if src_colorspace != dst_colorspace or src_color_range != dst_color_range: - with nogil: + with cython.nogil: ret = lib.sws_getColorspaceDetails( self.ptr, - &inv_tbl, - &src_colorspace_range, - &tbl, - &dst_colorspace_range, - &brightness, - &contrast, - &saturation + cython.address(inv_tbl), + cython.address(src_colorspace_range), + cython.address(tbl), + cython.address(dst_colorspace_range), + cython.address(brightness), + cython.address(contrast), + cython.address(saturation), ) - err_check(ret) - with nogil: + with cython.nogil: # Grab the coefficients for the requested transforms. # The inv_table brings us to linear, and `tbl` to the new space. if src_colorspace != lib.SWS_CS_DEFAULT: @@ -186,7 +215,6 @@ def reformat(self, VideoFrame frame not None, width=None, height=None, if dst_colorspace != lib.SWS_CS_DEFAULT: tbl = lib.sws_getCoefficients(dst_colorspace) - # Apply! ret = lib.sws_setColorspaceDetails( self.ptr, inv_tbl, @@ -195,21 +223,18 @@ def reformat(self, VideoFrame frame not None, width=None, height=None, dst_color_range, brightness, contrast, - saturation + saturation, ) - err_check(ret) - # Create a new VideoFrame. - cdef VideoFrame new_frame = alloc_video_frame() + new_frame: VideoFrame = alloc_video_frame() new_frame._copy_internal_attributes(frame) new_frame._init(dst_format, width, height) - # Finally, scale the image. - with nogil: + with cython.nogil: lib.sws_scale( self.ptr, - frame.ptr.data, + frame.ptr.data, frame.ptr.linesize, 0, # slice Y frame.ptr.height,