Voice et bot modif
This commit is contained in:
parent
189d56026b
commit
7333a22bcd
10774 changed files with 634644 additions and 933308 deletions
|
|
@ -2,7 +2,6 @@ from .codec import (
|
|||
Capabilities,
|
||||
Codec,
|
||||
Properties,
|
||||
codec_descriptor,
|
||||
codecs_available,
|
||||
find_best_pix_fmt_of_list,
|
||||
)
|
||||
|
|
@ -12,7 +11,6 @@ __all__ = (
|
|||
"Capabilities",
|
||||
"Codec",
|
||||
"Properties",
|
||||
"codec_descriptor",
|
||||
"codecs_available",
|
||||
"find_best_pix_fmt_of_list",
|
||||
"CodecContext",
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
venv/lib/python3.12/site-packages/av/codec/codec.abi3.so
Executable file
BIN
venv/lib/python3.12/site-packages/av/codec/codec.abi3.so
Executable file
Binary file not shown.
Binary file not shown.
|
|
@ -1,24 +1,25 @@
|
|||
cimport libav as lib
|
||||
|
||||
from av.audio.format cimport get_audio_format
|
||||
from av.codec.hwaccel cimport wrap_hwconfig
|
||||
from av.descriptor cimport wrap_avclass
|
||||
from av.utils cimport avrational_to_fraction
|
||||
from av.video.format cimport VideoFormat, get_pix_fmt, get_video_format
|
||||
|
||||
from enum import Flag, IntEnum
|
||||
from libc.stdlib cimport free, malloc
|
||||
|
||||
import cython
|
||||
from cython.cimports import libav as lib
|
||||
from cython.cimports.av.audio.format import get_audio_format
|
||||
from cython.cimports.av.codec.hwaccel import wrap_hwconfig
|
||||
from cython.cimports.av.utils import avrational_to_fraction
|
||||
from cython.cimports.av.video.format import VideoFormat, get_pix_fmt, get_video_format
|
||||
from cython.cimports.libc.stdlib import free, malloc
|
||||
|
||||
_cinit_sentinel = cython.declare(object, object())
|
||||
|
||||
|
||||
cdef object _cinit_sentinel = object()
|
||||
|
||||
cdef Codec wrap_codec(const lib.AVCodec *ptr):
|
||||
cdef Codec codec = Codec(_cinit_sentinel)
|
||||
@cython.cfunc
|
||||
def wrap_codec(ptr: cython.pointer[cython.const[lib.AVCodec]]) -> Codec:
|
||||
codec: Codec = Codec(_cinit_sentinel)
|
||||
codec.ptr = ptr
|
||||
codec.is_encoder = lib.av_codec_is_encoder(ptr)
|
||||
codec._init()
|
||||
return codec
|
||||
|
||||
|
||||
class Properties(Flag):
|
||||
NONE = 0
|
||||
INTRA_ONLY = lib.AV_CODEC_PROP_INTRA_ONLY
|
||||
|
|
@ -57,7 +58,9 @@ class UnknownCodecError(ValueError):
|
|||
pass
|
||||
|
||||
|
||||
cdef class Codec:
|
||||
@cython.final
|
||||
@cython.cclass
|
||||
class Codec:
|
||||
"""Codec(name, mode='r')
|
||||
|
||||
:param str name: The codec name.
|
||||
|
|
@ -105,7 +108,8 @@ cdef class Codec:
|
|||
if (mode == "w") != self.is_encoder:
|
||||
raise RuntimeError("Found codec does not match mode.", name, mode)
|
||||
|
||||
cdef _init(self, name=None):
|
||||
@cython.cfunc
|
||||
def _init(self, name=None):
|
||||
if not self.ptr:
|
||||
raise UnknownCodecError(name)
|
||||
|
||||
|
|
@ -124,12 +128,13 @@ cdef class Codec:
|
|||
mode = self.mode
|
||||
return f"<av.{self.__class__.__name__} {self.name} {mode=}>"
|
||||
|
||||
def create(self, kind = None):
|
||||
def create(self, kind=None):
|
||||
"""Create a :class:`.CodecContext` for this codec.
|
||||
|
||||
:param str kind: Gives a hint to static type checkers for what exact CodecContext is used.
|
||||
"""
|
||||
from .context import CodecContext
|
||||
|
||||
return CodecContext.create(self)
|
||||
|
||||
@property
|
||||
|
|
@ -141,10 +146,8 @@ cdef class Codec:
|
|||
return not self.is_encoder
|
||||
|
||||
@property
|
||||
def descriptor(self): return wrap_avclass(self.ptr.priv_class)
|
||||
|
||||
@property
|
||||
def name(self): return self.ptr.name or ""
|
||||
def name(self):
|
||||
return self.ptr.name or ""
|
||||
|
||||
@property
|
||||
def canonical_name(self):
|
||||
|
|
@ -154,7 +157,8 @@ cdef class Codec:
|
|||
return lib.avcodec_get_name(self.ptr.id)
|
||||
|
||||
@property
|
||||
def long_name(self): return self.ptr.long_name or ""
|
||||
def long_name(self):
|
||||
return self.ptr.long_name or ""
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
|
|
@ -164,79 +168,100 @@ cdef class Codec:
|
|||
E.g: ``'audio'``, ``'video'``, ``'subtitle'``.
|
||||
|
||||
"""
|
||||
return lib.av_get_media_type_string(self.ptr.type)
|
||||
media_type = lib.av_get_media_type_string(self.ptr.type)
|
||||
return "unknown" if media_type == cython.NULL else media_type
|
||||
|
||||
@property
|
||||
def id(self): return self.ptr.id
|
||||
def id(self):
|
||||
return self.ptr.id
|
||||
|
||||
@property
|
||||
def frame_rates(self):
|
||||
"""A list of supported frame rates (:class:`fractions.Fraction`), or ``None``."""
|
||||
if not self.ptr.supported_framerates:
|
||||
out: cython.pointer[cython.const[cython.void]] = cython.NULL
|
||||
num: cython.int = 0
|
||||
lib.avcodec_get_supported_config(
|
||||
cython.NULL,
|
||||
self.ptr,
|
||||
lib.AV_CODEC_CONFIG_FRAME_RATE,
|
||||
0,
|
||||
cython.address(out),
|
||||
cython.address(num),
|
||||
)
|
||||
if not out:
|
||||
return
|
||||
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
while self.ptr.supported_framerates[i].denum:
|
||||
ret.append(avrational_to_fraction(&self.ptr.supported_framerates[i]))
|
||||
i += 1
|
||||
return ret
|
||||
rates = cython.cast(cython.pointer[lib.AVRational], out)
|
||||
return [avrational_to_fraction(cython.address(rates[i])) for i in range(num)]
|
||||
|
||||
@property
|
||||
def audio_rates(self):
|
||||
"""A list of supported audio sample rates (``int``), or ``None``."""
|
||||
if not self.ptr.supported_samplerates:
|
||||
out: cython.pointer[cython.const[cython.void]] = cython.NULL
|
||||
num: cython.int = 0
|
||||
lib.avcodec_get_supported_config(
|
||||
cython.NULL,
|
||||
self.ptr,
|
||||
lib.AV_CODEC_CONFIG_SAMPLE_RATE,
|
||||
0,
|
||||
cython.address(out),
|
||||
cython.address(num),
|
||||
)
|
||||
if not out:
|
||||
return
|
||||
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
while self.ptr.supported_samplerates[i]:
|
||||
ret.append(self.ptr.supported_samplerates[i])
|
||||
i += 1
|
||||
return ret
|
||||
rates = cython.cast(cython.pointer[cython.int], out)
|
||||
return [rates[i] for i in range(num)]
|
||||
|
||||
@property
|
||||
def video_formats(self):
|
||||
"""A list of supported :class:`.VideoFormat`, or ``None``."""
|
||||
if not self.ptr.pix_fmts:
|
||||
out: cython.pointer[cython.const[cython.void]] = cython.NULL
|
||||
num: cython.int = 0
|
||||
lib.avcodec_get_supported_config(
|
||||
cython.NULL,
|
||||
self.ptr,
|
||||
lib.AV_CODEC_CONFIG_PIX_FORMAT,
|
||||
0,
|
||||
cython.address(out),
|
||||
cython.address(num),
|
||||
)
|
||||
if not out:
|
||||
return
|
||||
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
while self.ptr.pix_fmts[i] != -1:
|
||||
ret.append(get_video_format(self.ptr.pix_fmts[i], 0, 0))
|
||||
i += 1
|
||||
return ret
|
||||
fmts = cython.cast(cython.pointer[lib.AVPixelFormat], out)
|
||||
return [get_video_format(fmts[i], 0, 0) for i in range(num)]
|
||||
|
||||
@property
|
||||
def audio_formats(self):
|
||||
"""A list of supported :class:`.AudioFormat`, or ``None``."""
|
||||
if not self.ptr.sample_fmts:
|
||||
out: cython.pointer[cython.const[cython.void]] = cython.NULL
|
||||
num: cython.int = 0
|
||||
lib.avcodec_get_supported_config(
|
||||
cython.NULL,
|
||||
self.ptr,
|
||||
lib.AV_CODEC_CONFIG_SAMPLE_FORMAT,
|
||||
0,
|
||||
cython.address(out),
|
||||
cython.address(num),
|
||||
)
|
||||
if not out:
|
||||
return
|
||||
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
while self.ptr.sample_fmts[i] != -1:
|
||||
ret.append(get_audio_format(self.ptr.sample_fmts[i]))
|
||||
i += 1
|
||||
return ret
|
||||
fmts = cython.cast(cython.pointer[lib.AVSampleFormat], out)
|
||||
return [get_audio_format(fmts[i]) for i in range(num)]
|
||||
|
||||
@property
|
||||
def hardware_configs(self):
|
||||
if self._hardware_configs:
|
||||
return self._hardware_configs
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
cdef const lib.AVCodecHWConfig *ptr
|
||||
ret: list = []
|
||||
i: cython.int = 0
|
||||
ptr: cython.pointer[cython.const[lib.AVCodecHWConfig]]
|
||||
while True:
|
||||
ptr = lib.avcodec_get_hw_config(self.ptr, i)
|
||||
if not ptr:
|
||||
break
|
||||
ret.append(wrap_hwconfig(ptr))
|
||||
i += 1
|
||||
ret = tuple(ret)
|
||||
self._hardware_configs = ret
|
||||
return ret
|
||||
self._hardware_configs = tuple(ret)
|
||||
return self._hardware_configs
|
||||
|
||||
@property
|
||||
def properties(self):
|
||||
|
|
@ -309,12 +334,14 @@ cdef class Codec:
|
|||
"""
|
||||
return bool(self.ptr.capabilities & lib.AV_CODEC_CAP_DELAY)
|
||||
|
||||
cdef get_codec_names():
|
||||
names = set()
|
||||
cdef const lib.AVCodec *ptr
|
||||
cdef void *opaque = NULL
|
||||
|
||||
@cython.cfunc
|
||||
def get_codec_names():
|
||||
names: cython.set = set()
|
||||
ptr = cython.declare(cython.pointer[cython.const[lib.AVCodec]])
|
||||
opaque: cython.p_void = cython.NULL
|
||||
while True:
|
||||
ptr = lib.av_codec_iterate(&opaque)
|
||||
ptr = lib.av_codec_iterate(cython.address(opaque))
|
||||
if ptr:
|
||||
names.add(ptr.name)
|
||||
else:
|
||||
|
|
@ -323,7 +350,6 @@ cdef get_codec_names():
|
|||
|
||||
|
||||
codecs_available = get_codec_names()
|
||||
codec_descriptor = wrap_avclass(lib.avcodec_get_class())
|
||||
|
||||
|
||||
def dump_codecs():
|
||||
|
|
@ -373,6 +399,7 @@ def dump_codecs():
|
|||
except Exception as e:
|
||||
print(f"...... {codec.name:<18} ERROR: {e}")
|
||||
|
||||
|
||||
def dump_hwconfigs():
|
||||
print("Hardware configs:")
|
||||
for name in sorted(codecs_available):
|
||||
|
|
@ -402,13 +429,13 @@ def find_best_pix_fmt_of_list(pix_fmts, src_pix_fmt, has_alpha=False):
|
|||
:return: (best_format, loss)
|
||||
:rtype: (VideoFormat | None, int)
|
||||
"""
|
||||
cdef lib.AVPixelFormat src
|
||||
cdef lib.AVPixelFormat best
|
||||
cdef lib.AVPixelFormat *c_list = NULL
|
||||
cdef Py_ssize_t n
|
||||
cdef Py_ssize_t i
|
||||
cdef object item
|
||||
cdef int c_loss
|
||||
src: lib.AVPixelFormat
|
||||
best: lib.AVPixelFormat
|
||||
c_list: cython.pointer[lib.AVPixelFormat] = cython.NULL
|
||||
n: cython.Py_ssize_t
|
||||
i: cython.Py_ssize_t
|
||||
item: object
|
||||
c_loss: cython.int
|
||||
|
||||
if pix_fmts is None:
|
||||
raise TypeError("pix_fmts must not be None")
|
||||
|
|
@ -418,29 +445,32 @@ def find_best_pix_fmt_of_list(pix_fmts, src_pix_fmt, has_alpha=False):
|
|||
return None, 0
|
||||
|
||||
if isinstance(src_pix_fmt, VideoFormat):
|
||||
src = (<VideoFormat>src_pix_fmt).pix_fmt
|
||||
src = cython.cast(VideoFormat, src_pix_fmt).pix_fmt
|
||||
else:
|
||||
src = get_pix_fmt(<str>src_pix_fmt)
|
||||
src = get_pix_fmt(cython.cast(str, src_pix_fmt))
|
||||
|
||||
n = len(pix_fmts)
|
||||
c_list = <lib.AVPixelFormat *>malloc((n + 1) * sizeof(lib.AVPixelFormat))
|
||||
if c_list == NULL:
|
||||
c_list = cython.cast(
|
||||
cython.pointer[lib.AVPixelFormat],
|
||||
malloc((n + 1) * cython.sizeof(lib.AVPixelFormat)),
|
||||
)
|
||||
if c_list == cython.NULL:
|
||||
raise MemoryError()
|
||||
|
||||
try:
|
||||
for i in range(n):
|
||||
item = pix_fmts[i]
|
||||
if isinstance(item, VideoFormat):
|
||||
c_list[i] = (<VideoFormat>item).pix_fmt
|
||||
c_list[i] = cython.cast(VideoFormat, item).pix_fmt
|
||||
else:
|
||||
c_list[i] = get_pix_fmt(<str>item)
|
||||
c_list[i] = get_pix_fmt(cython.cast(str, item))
|
||||
c_list[n] = lib.AV_PIX_FMT_NONE
|
||||
|
||||
c_loss = 0
|
||||
best = lib.avcodec_find_best_pix_fmt_of_list(
|
||||
c_list, src, 1 if has_alpha else 0, &c_loss
|
||||
c_list, src, 1 if has_alpha else 0, cython.address(c_loss)
|
||||
)
|
||||
return get_video_format(best, 0, 0), c_loss
|
||||
finally:
|
||||
if c_list != NULL:
|
||||
if c_list != cython.NULL:
|
||||
free(c_list)
|
||||
|
|
@ -4,7 +4,6 @@ from typing import ClassVar, Literal, Sequence, cast, overload
|
|||
|
||||
from av.audio.codeccontext import AudioCodecContext
|
||||
from av.audio.format import AudioFormat
|
||||
from av.descriptor import Descriptor
|
||||
from av.subtitles.codeccontext import SubtitleCodecContext
|
||||
from av.video.codeccontext import VideoCodecContext
|
||||
from av.video.format import VideoFormat
|
||||
|
|
@ -53,7 +52,6 @@ class Codec:
|
|||
def is_decoder(self) -> bool: ...
|
||||
@property
|
||||
def mode(self) -> Literal["r", "w"]: ...
|
||||
descriptor: Descriptor
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@property
|
||||
|
|
@ -61,7 +59,9 @@ class Codec:
|
|||
@property
|
||||
def long_name(self) -> str: ...
|
||||
@property
|
||||
def type(self) -> Literal["video", "audio", "data", "subtitle", "attachment"]: ...
|
||||
def type(
|
||||
self,
|
||||
) -> Literal["video", "audio", "data", "subtitle", "attachment", "unknown"]: ...
|
||||
@property
|
||||
def id(self) -> int: ...
|
||||
frame_rates: list[Fraction] | None
|
||||
|
|
@ -105,10 +105,6 @@ class Codec:
|
|||
VideoCodecContext | AudioCodecContext | SubtitleCodecContext | CodecContext
|
||||
): ...
|
||||
|
||||
class codec_descriptor:
|
||||
name: str
|
||||
options: tuple[int, ...]
|
||||
|
||||
codecs_available: set[str]
|
||||
|
||||
def dump_codecs() -> None: ...
|
||||
|
|
|
|||
BIN
venv/lib/python3.12/site-packages/av/codec/context.abi3.so
Executable file
BIN
venv/lib/python3.12/site-packages/av/codec/context.abi3.so
Executable file
Binary file not shown.
Binary file not shown.
|
|
@ -1,7 +1,7 @@
|
|||
cimport libav as lib
|
||||
from libc.stdint cimport int64_t
|
||||
from libc.stdint cimport int64_t, uint8_t
|
||||
|
||||
from av.bytesource cimport ByteSource
|
||||
from av.buffer cimport ByteSource
|
||||
from av.codec.codec cimport Codec
|
||||
from av.codec.hwaccel cimport HWAccel
|
||||
from av.frame cimport Frame
|
||||
|
|
@ -11,9 +11,6 @@ from av.packet cimport Packet
|
|||
cdef class CodecContext:
|
||||
cdef lib.AVCodecContext *ptr
|
||||
|
||||
# Whether AVCodecContext.extradata should be de-allocated upon destruction.
|
||||
cdef bint extradata_set
|
||||
|
||||
# Used as a signal that this is within a stream, and also for us to access that
|
||||
# stream. This is set "manually" by the stream after constructing this object.
|
||||
cdef int stream_index
|
||||
|
|
@ -36,11 +33,15 @@ cdef class CodecContext:
|
|||
# Used by hardware-accelerated decode.
|
||||
cdef HWAccel hwaccel_ctx
|
||||
|
||||
cdef uint8_t _ctxflags # ctxEnum: template_initialized
|
||||
# True when created via add_stream_from_template(); start_encoding() skips
|
||||
# avcodec_open2() and lets encode()/decode() open the codec lazily if needed.
|
||||
|
||||
# Used by both transcode APIs to setup user-land objects.
|
||||
# TODO: Remove the `Packet` from `_setup_decoded_frame` (because flushing packets
|
||||
# are bogus). It should take all info it needs from the context and/or stream.
|
||||
cdef _prepare_and_time_rebase_frames_for_encode(self, Frame frame)
|
||||
cdef _prepare_frames_for_encode(self, Frame frame)
|
||||
cdef list _prepare_frames_for_encode(self, Frame frame)
|
||||
cdef _setup_encoded_packet(self, Packet)
|
||||
cdef _setup_decoded_frame(self, Frame, Packet)
|
||||
|
||||
|
|
@ -51,7 +52,6 @@ cdef class CodecContext:
|
|||
# send/recv buffer may be limited to a single frame. Ergo, we need to flush
|
||||
# the buffer as often as possible.
|
||||
cdef _recv_packet(self)
|
||||
cdef _send_packet_and_recv(self, Packet packet)
|
||||
cdef _recv_frame(self)
|
||||
|
||||
cdef _transfer_hwframe(self, Frame frame)
|
||||
|
|
|
|||
|
|
@ -1,36 +1,40 @@
|
|||
cimport libav as lib
|
||||
from libc.errno cimport EAGAIN
|
||||
from libc.stdint cimport uint8_t
|
||||
from libc.string cimport memcpy
|
||||
|
||||
from av.bytesource cimport ByteSource, bytesource
|
||||
from av.codec.codec cimport Codec, wrap_codec
|
||||
from av.dictionary cimport _Dictionary
|
||||
from av.error cimport err_check
|
||||
from av.packet cimport Packet
|
||||
from av.utils cimport avrational_to_fraction, to_avrational
|
||||
|
||||
from enum import Flag, IntEnum
|
||||
|
||||
from av.dictionary import Dictionary
|
||||
import cython
|
||||
from cython.cimports import libav as lib
|
||||
from cython.cimports.av.buffer import ByteSource, bytesource
|
||||
from cython.cimports.av.codec.codec import Codec, wrap_codec
|
||||
from cython.cimports.av.dictionary import Dictionary
|
||||
from cython.cimports.av.error import err_check
|
||||
from cython.cimports.av.packet import Packet
|
||||
from cython.cimports.av.utils import avrational_to_fraction, to_avrational
|
||||
from cython.cimports.libc.errno import EAGAIN
|
||||
from cython.cimports.libc.stdint import uint8_t
|
||||
from cython.cimports.libc.string import memcpy
|
||||
|
||||
_cinit_sentinel = cython.declare(object, object())
|
||||
|
||||
|
||||
cdef object _cinit_sentinel = object()
|
||||
|
||||
|
||||
cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCodec *c_codec, HWAccel hwaccel):
|
||||
"""Build an av.CodecContext for an existing AVCodecContext."""
|
||||
|
||||
cdef CodecContext py_ctx
|
||||
@cython.cfunc
|
||||
def wrap_codec_context(
|
||||
c_ctx: cython.pointer[lib.AVCodecContext],
|
||||
c_codec: cython.pointer[cython.const[lib.AVCodec]],
|
||||
hwaccel: HWAccel,
|
||||
) -> CodecContext:
|
||||
"""Build an bv.CodecContext for an existing AVCodecContext."""
|
||||
py_ctx: CodecContext
|
||||
|
||||
if c_ctx.codec_type == lib.AVMEDIA_TYPE_VIDEO:
|
||||
from av.video.codeccontext import VideoCodecContext
|
||||
|
||||
py_ctx = VideoCodecContext(_cinit_sentinel)
|
||||
elif c_ctx.codec_type == lib.AVMEDIA_TYPE_AUDIO:
|
||||
from av.audio.codeccontext import AudioCodecContext
|
||||
|
||||
py_ctx = AudioCodecContext(_cinit_sentinel)
|
||||
elif c_ctx.codec_type == lib.AVMEDIA_TYPE_SUBTITLE:
|
||||
from av.subtitles.codeccontext import SubtitleCodecContext
|
||||
|
||||
py_ctx = SubtitleCodecContext(_cinit_sentinel)
|
||||
else:
|
||||
py_ctx = CodecContext(_cinit_sentinel)
|
||||
|
|
@ -44,7 +48,10 @@ class ThreadType(Flag):
|
|||
NONE = 0
|
||||
FRAME: "Decode more than one frame at once" = lib.FF_THREAD_FRAME
|
||||
SLICE: "Decode more than one part of a single frame at once" = lib.FF_THREAD_SLICE
|
||||
AUTO: "Decode using both FRAME and SLICE methods." = lib.FF_THREAD_SLICE | lib.FF_THREAD_FRAME
|
||||
AUTO: "Decode using both FRAME and SLICE methods." = (
|
||||
lib.FF_THREAD_SLICE | lib.FF_THREAD_FRAME
|
||||
)
|
||||
|
||||
|
||||
class Flags(IntEnum):
|
||||
unaligned = lib.AV_CODEC_FLAG_UNALIGNED
|
||||
|
|
@ -68,6 +75,7 @@ class Flags(IntEnum):
|
|||
interlaced_me = lib.AV_CODEC_FLAG_INTERLACED_ME
|
||||
closed_gop = lib.AV_CODEC_FLAG_CLOSED_GOP
|
||||
|
||||
|
||||
class Flags2(IntEnum):
|
||||
fast = lib.AV_CODEC_FLAG2_FAST
|
||||
no_output = lib.AV_CODEC_FLAG2_NO_OUTPUT
|
||||
|
|
@ -80,11 +88,14 @@ class Flags2(IntEnum):
|
|||
ro_flush_noop = lib.AV_CODEC_FLAG2_RO_FLUSH_NOOP
|
||||
|
||||
|
||||
cdef class CodecContext:
|
||||
@cython.cclass
|
||||
class CodecContext:
|
||||
@staticmethod
|
||||
def create(codec, mode=None, hwaccel=None):
|
||||
cdef Codec cy_codec = codec if isinstance(codec, Codec) else Codec(codec, mode)
|
||||
cdef lib.AVCodecContext *c_ctx = lib.avcodec_alloc_context3(cy_codec.ptr)
|
||||
cy_codec: Codec = codec if isinstance(codec, Codec) else Codec(codec, mode)
|
||||
c_ctx: cython.pointer[lib.AVCodecContext] = lib.avcodec_alloc_context3(
|
||||
cy_codec.ptr
|
||||
)
|
||||
return wrap_codec_context(c_ctx, cy_codec.ptr, hwaccel)
|
||||
|
||||
def __cinit__(self, sentinel=None, *args, **kwargs):
|
||||
|
|
@ -95,11 +106,17 @@ cdef class CodecContext:
|
|||
self.stream_index = -1 # This is set by the container immediately.
|
||||
self.is_open = False
|
||||
|
||||
cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec, HWAccel hwaccel):
|
||||
@cython.cfunc
|
||||
def _init(
|
||||
self,
|
||||
ptr: cython.pointer[lib.AVCodecContext],
|
||||
codec: cython.pointer[cython.const[lib.AVCodec]],
|
||||
hwaccel: HWAccel,
|
||||
):
|
||||
self.ptr = ptr
|
||||
if self.ptr.codec and codec and self.ptr.codec != codec:
|
||||
raise RuntimeError("Wrapping CodecContext with mismatched codec.")
|
||||
self.codec = wrap_codec(codec if codec != NULL else self.ptr.codec)
|
||||
self.codec = wrap_codec(codec if codec != cython.NULL else self.ptr.codec)
|
||||
self.hwaccel = hwaccel
|
||||
|
||||
# Set reasonable threading defaults.
|
||||
|
|
@ -116,7 +133,7 @@ cdef class CodecContext:
|
|||
return self.ptr.flags
|
||||
|
||||
@flags.setter
|
||||
def flags(self, int value):
|
||||
def flags(self, value: cython.int):
|
||||
self.ptr.flags = value
|
||||
|
||||
@property
|
||||
|
|
@ -156,30 +173,39 @@ cdef class CodecContext:
|
|||
return self.ptr.flags2
|
||||
|
||||
@flags2.setter
|
||||
def flags2(self, int value):
|
||||
def flags2(self, value: cython.int):
|
||||
self.ptr.flags2 = value
|
||||
|
||||
@property
|
||||
def extradata(self):
|
||||
if self.ptr is NULL:
|
||||
if self.ptr is cython.NULL:
|
||||
return None
|
||||
if self.ptr.extradata_size > 0:
|
||||
return <bytes>(<uint8_t*>self.ptr.extradata)[:self.ptr.extradata_size]
|
||||
return cython.cast(
|
||||
bytes,
|
||||
cython.cast(cython.pointer[uint8_t], self.ptr.extradata)[
|
||||
: self.ptr.extradata_size
|
||||
],
|
||||
)
|
||||
return None
|
||||
|
||||
@extradata.setter
|
||||
def extradata(self, data):
|
||||
if data is None:
|
||||
lib.av_freep(&self.ptr.extradata)
|
||||
lib.av_freep(cython.address(self.ptr.extradata))
|
||||
self.ptr.extradata_size = 0
|
||||
else:
|
||||
source = bytesource(data)
|
||||
self.ptr.extradata = <uint8_t*>lib.av_realloc(self.ptr.extradata, source.length + lib.AV_INPUT_BUFFER_PADDING_SIZE)
|
||||
self.ptr.extradata = cython.cast(
|
||||
cython.pointer[uint8_t],
|
||||
lib.av_realloc(
|
||||
self.ptr.extradata, source.length + lib.AV_INPUT_BUFFER_PADDING_SIZE
|
||||
),
|
||||
)
|
||||
if not self.ptr.extradata:
|
||||
raise MemoryError("Cannot allocate extradata")
|
||||
memcpy(self.ptr.extradata, source.ptr, source.length)
|
||||
self.ptr.extradata_size = source.length
|
||||
self.extradata_set = True
|
||||
|
||||
@property
|
||||
def extradata_size(self):
|
||||
|
|
@ -187,23 +213,24 @@ cdef class CodecContext:
|
|||
|
||||
@property
|
||||
def is_encoder(self):
|
||||
if self.ptr is NULL:
|
||||
if self.ptr is cython.NULL:
|
||||
return False
|
||||
return lib.av_codec_is_encoder(self.ptr.codec)
|
||||
|
||||
@property
|
||||
def is_decoder(self):
|
||||
if self.ptr is NULL:
|
||||
if self.ptr is cython.NULL:
|
||||
return False
|
||||
return lib.av_codec_is_decoder(self.ptr.codec)
|
||||
|
||||
cpdef open(self, bint strict=True):
|
||||
@cython.ccall
|
||||
def open(self, strict: cython.bint = True):
|
||||
if self.is_open:
|
||||
if strict:
|
||||
raise ValueError("CodecContext is already open.")
|
||||
return
|
||||
|
||||
cdef _Dictionary options = Dictionary()
|
||||
options: Dictionary = Dictionary()
|
||||
options.update(self.options or {})
|
||||
|
||||
if not self.ptr.time_base.num and self.is_encoder:
|
||||
|
|
@ -217,15 +244,17 @@ cdef class CodecContext:
|
|||
self.ptr.time_base.num = 1
|
||||
self.ptr.time_base.den = lib.AV_TIME_BASE
|
||||
|
||||
err_check(lib.avcodec_open2(self.ptr, self.codec.ptr, &options.ptr), "avcodec_open2(" + self.codec.name + ")")
|
||||
err_check(
|
||||
lib.avcodec_open2(self.ptr, self.codec.ptr, cython.address(options.ptr)),
|
||||
f'avcodec_open2("{self.codec.name}", {self.options})',
|
||||
)
|
||||
self.is_open = True
|
||||
self.options = dict(options)
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.ptr and self.extradata_set:
|
||||
lib.av_freep(&self.ptr.extradata)
|
||||
if self.ptr:
|
||||
lib.avcodec_free_context(&self.ptr)
|
||||
lib.av_freep(cython.address(self.ptr.extradata))
|
||||
lib.avcodec_free_context(cython.address(self.ptr))
|
||||
if self.parser:
|
||||
lib.av_parser_close(self.parser)
|
||||
|
||||
|
|
@ -243,6 +272,10 @@ cdef class CodecContext:
|
|||
It will return all packets that are fully contained within the given
|
||||
input, and will buffer partial packets until they are complete.
|
||||
|
||||
Any timing information the parser is able to infer (``pts``, ``dts``,
|
||||
``duration``, ``pos`` and the keyframe flag) is assigned onto the
|
||||
returned packets. Fields the parser cannot determine are left unset.
|
||||
|
||||
:param ByteSource raw_input: A chunk of a byte-stream to process.
|
||||
Anything that can be turned into a :class:`.ByteSource` is fine.
|
||||
``None`` or empty inputs will flush the parser's buffers.
|
||||
|
|
@ -256,27 +289,29 @@ cdef class CodecContext:
|
|||
if not self.parser:
|
||||
raise ValueError(f"No parser for {self.codec.name}")
|
||||
|
||||
cdef ByteSource source = bytesource(raw_input, allow_none=True)
|
||||
source: ByteSource = bytesource(raw_input, allow_none=True)
|
||||
|
||||
cdef unsigned char *in_data = source.ptr if source is not None else NULL
|
||||
cdef int in_size = source.length if source is not None else 0
|
||||
in_data: cython.p_uchar = source.ptr if source is not None else cython.NULL
|
||||
in_size: cython.int = source.length if source is not None else 0
|
||||
|
||||
cdef unsigned char *out_data
|
||||
cdef int out_size
|
||||
cdef int consumed
|
||||
cdef Packet packet = None
|
||||
|
||||
packets = []
|
||||
out_data: cython.p_uchar
|
||||
out_size: cython.int
|
||||
consumed: cython.int
|
||||
packet: Packet = None
|
||||
packets: list = []
|
||||
|
||||
while True:
|
||||
with nogil:
|
||||
with cython.nogil:
|
||||
consumed = lib.av_parser_parse2(
|
||||
self.parser,
|
||||
self.ptr,
|
||||
&out_data, &out_size,
|
||||
in_data, in_size,
|
||||
lib.AV_NOPTS_VALUE, lib.AV_NOPTS_VALUE,
|
||||
0
|
||||
cython.address(out_data),
|
||||
cython.address(out_size),
|
||||
in_data,
|
||||
in_size,
|
||||
lib.AV_NOPTS_VALUE,
|
||||
lib.AV_NOPTS_VALUE,
|
||||
0,
|
||||
)
|
||||
err_check(consumed)
|
||||
|
||||
|
|
@ -296,6 +331,16 @@ cdef class CodecContext:
|
|||
packet = Packet(out_size)
|
||||
memcpy(packet.ptr.data, out_data, out_size)
|
||||
|
||||
# Propagate the timing information the parser inferred for
|
||||
# this frame onto the packet (mirrors FFmpeg's parse_packet).
|
||||
packet.ptr.pts = self.parser.pts
|
||||
packet.ptr.dts = self.parser.dts
|
||||
packet.ptr.pos = self.parser.pos
|
||||
if self.parser.duration:
|
||||
packet.ptr.duration = self.parser.duration
|
||||
if self.parser.key_frame == 1:
|
||||
packet.ptr.flags |= lib.AV_PKT_FLAG_KEY
|
||||
|
||||
packets.append(packet)
|
||||
|
||||
if not in_size:
|
||||
|
|
@ -317,12 +362,13 @@ cdef class CodecContext:
|
|||
"""
|
||||
return self.hwaccel_ctx is not None
|
||||
|
||||
def _send_frame_and_recv(self, Frame frame):
|
||||
cdef Packet packet
|
||||
|
||||
cdef int res
|
||||
with nogil:
|
||||
res = lib.avcodec_send_frame(self.ptr, frame.ptr if frame is not None else NULL)
|
||||
def _send_frame_and_recv(self, frame: Frame | None):
|
||||
packet: Packet
|
||||
res: cython.int
|
||||
with cython.nogil:
|
||||
res = lib.avcodec_send_frame(
|
||||
self.ptr, frame.ptr if frame is not None else cython.NULL
|
||||
)
|
||||
err_check(res, "avcodec_send_frame()")
|
||||
|
||||
packet = self._recv_packet()
|
||||
|
|
@ -330,65 +376,56 @@ cdef class CodecContext:
|
|||
yield packet
|
||||
packet = self._recv_packet()
|
||||
|
||||
cdef _send_packet_and_recv(self, Packet packet):
|
||||
cdef Frame frame
|
||||
|
||||
cdef int res
|
||||
with nogil:
|
||||
res = lib.avcodec_send_packet(self.ptr, packet.ptr if packet is not None else NULL)
|
||||
err_check(res, "avcodec_send_packet()")
|
||||
|
||||
out = []
|
||||
while True:
|
||||
frame = self._recv_frame()
|
||||
if frame:
|
||||
out.append(frame)
|
||||
else:
|
||||
break
|
||||
return out
|
||||
|
||||
cdef _prepare_frames_for_encode(self, Frame frame):
|
||||
@cython.cfunc
|
||||
def _prepare_frames_for_encode(self, frame: Frame | None) -> list:
|
||||
return [frame]
|
||||
|
||||
cdef Frame _alloc_next_frame(self):
|
||||
@cython.cfunc
|
||||
def _alloc_next_frame(self) -> Frame:
|
||||
raise NotImplementedError("Base CodecContext cannot decode.")
|
||||
|
||||
cdef _recv_frame(self):
|
||||
@cython.cfunc
|
||||
def _recv_frame(self):
|
||||
if not self._next_frame:
|
||||
self._next_frame = self._alloc_next_frame()
|
||||
cdef Frame frame = self._next_frame
|
||||
|
||||
cdef int res
|
||||
with nogil:
|
||||
frame: Frame = self._next_frame
|
||||
res: cython.int
|
||||
|
||||
with cython.nogil:
|
||||
res = lib.avcodec_receive_frame(self.ptr, frame.ptr)
|
||||
|
||||
if res == -EAGAIN or res == lib.AVERROR_EOF:
|
||||
return
|
||||
err_check(res, "avcodec_receive_frame()")
|
||||
|
||||
err_check(res, "avcodec_receive_frame()")
|
||||
frame = self._transfer_hwframe(frame)
|
||||
|
||||
if not res:
|
||||
self._next_frame = None
|
||||
return frame
|
||||
|
||||
cdef _transfer_hwframe(self, Frame frame):
|
||||
@cython.cfunc
|
||||
def _transfer_hwframe(self, frame: Frame):
|
||||
return frame
|
||||
|
||||
cdef _recv_packet(self):
|
||||
cdef Packet packet = Packet()
|
||||
@cython.cfunc
|
||||
def _recv_packet(self):
|
||||
packet: Packet = Packet()
|
||||
res: cython.int
|
||||
|
||||
cdef int res
|
||||
with nogil:
|
||||
with cython.nogil:
|
||||
res = lib.avcodec_receive_packet(self.ptr, packet.ptr)
|
||||
|
||||
if res == -EAGAIN or res == lib.AVERROR_EOF:
|
||||
return
|
||||
err_check(res, "avcodec_receive_packet()")
|
||||
|
||||
err_check(res, "avcodec_receive_packet()")
|
||||
if not res:
|
||||
return packet
|
||||
|
||||
cdef _prepare_and_time_rebase_frames_for_encode(self, Frame frame):
|
||||
@cython.cfunc
|
||||
def _prepare_and_time_rebase_frames_for_encode(self, frame: Frame):
|
||||
if self.ptr.codec_type not in [lib.AVMEDIA_TYPE_VIDEO, lib.AVMEDIA_TYPE_AUDIO]:
|
||||
raise NotImplementedError("Encoding is only supported for audio and video.")
|
||||
|
||||
|
|
@ -404,7 +441,8 @@ cdef class CodecContext:
|
|||
|
||||
return frames
|
||||
|
||||
cpdef encode(self, Frame frame=None):
|
||||
@cython.ccall
|
||||
def encode(self, frame: Frame | None = None):
|
||||
"""Encode a list of :class:`.Packet` from the given :class:`.Frame`."""
|
||||
res = []
|
||||
for frame in self._prepare_and_time_rebase_frames_for_encode(frame):
|
||||
|
|
@ -413,13 +451,14 @@ cdef class CodecContext:
|
|||
res.append(packet)
|
||||
return res
|
||||
|
||||
def encode_lazy(self, Frame frame=None):
|
||||
def encode_lazy(self, frame: Frame | None = None):
|
||||
for frame in self._prepare_and_time_rebase_frames_for_encode(frame):
|
||||
for packet in self._send_frame_and_recv(frame):
|
||||
self._setup_encoded_packet(packet)
|
||||
yield packet
|
||||
|
||||
cdef _setup_encoded_packet(self, Packet packet):
|
||||
@cython.cfunc
|
||||
def _setup_encoded_packet(self, packet: Packet):
|
||||
# We coerced the frame's time_base into the CodecContext's during encoding,
|
||||
# and FFmpeg copied the frame's pts/dts to the packet, so keep track of
|
||||
# this time_base in case the frame needs to be muxed to a container with
|
||||
|
|
@ -429,28 +468,45 @@ cdef class CodecContext:
|
|||
# are off!
|
||||
packet.ptr.time_base = self.ptr.time_base
|
||||
|
||||
cpdef decode(self, Packet packet=None):
|
||||
@cython.ccall
|
||||
def decode(self, packet: Packet | None = None):
|
||||
"""Decode a list of :class:`.Frame` from the given :class:`.Packet`.
|
||||
|
||||
If the packet is None, the buffers will be flushed. This is useful if
|
||||
you do not want the library to automatically re-order frames for you
|
||||
(if they are encoded with a codec that has B-frames).
|
||||
|
||||
"""
|
||||
.. warning::
|
||||
|
||||
This method is **not thread-safe**. Calling :meth:`decode` concurrently
|
||||
from multiple threads on the same :class:`CodecContext` will corrupt
|
||||
internal FFmpeg state and likely cause a crash (segfault). FFmpeg 8.1
|
||||
enforces this more strictly than earlier releases. If you need to decode
|
||||
from multiple threads, give each thread its own :class:`CodecContext`.
|
||||
|
||||
"""
|
||||
if not self.codec.ptr:
|
||||
raise ValueError("cannot decode unknown codec")
|
||||
|
||||
self.open(strict=False)
|
||||
|
||||
res = []
|
||||
for frame in self._send_packet_and_recv(packet):
|
||||
if isinstance(frame, Frame):
|
||||
self._setup_decoded_frame(frame, packet)
|
||||
res.append(frame)
|
||||
return res
|
||||
res: cython.int
|
||||
with cython.nogil:
|
||||
res = lib.avcodec_send_packet(
|
||||
self.ptr, packet.ptr if packet is not None else cython.NULL
|
||||
)
|
||||
err_check(res, "avcodec_send_packet()")
|
||||
|
||||
cpdef flush_buffers(self):
|
||||
out: list = []
|
||||
frame = self._recv_frame()
|
||||
while frame:
|
||||
self._setup_decoded_frame(frame, packet)
|
||||
out.append(frame)
|
||||
frame = self._recv_frame()
|
||||
return out
|
||||
|
||||
@cython.ccall
|
||||
def flush_buffers(self):
|
||||
"""Reset the internal codec state and discard all internal buffers.
|
||||
|
||||
Should be called before you start decoding from a new position e.g.
|
||||
|
|
@ -458,10 +514,11 @@ cdef class CodecContext:
|
|||
|
||||
"""
|
||||
if self.is_open:
|
||||
with nogil:
|
||||
with cython.nogil:
|
||||
lib.avcodec_flush_buffers(self.ptr)
|
||||
|
||||
cdef _setup_decoded_frame(self, Frame frame, Packet packet):
|
||||
@cython.cfunc
|
||||
def _setup_decoded_frame(self, frame: Frame, packet: Packet | None):
|
||||
# Propagate our manual times.
|
||||
# While decoding, frame times are in stream time_base, which PyAV
|
||||
# is carrying around.
|
||||
|
|
@ -485,15 +542,15 @@ cdef class CodecContext:
|
|||
|
||||
:type: list[str]
|
||||
"""
|
||||
ret = []
|
||||
ret: list = []
|
||||
if not self.ptr.codec or not self.codec.desc or not self.codec.desc.profiles:
|
||||
return ret
|
||||
|
||||
# Profiles are always listed in the codec descriptor, but not necessarily in
|
||||
# the codec itself. So use the descriptor here.
|
||||
desc = self.codec.desc
|
||||
cdef int i = 0
|
||||
while desc.profiles[i].profile != lib.AV_PROFILE_UNKNOWN:
|
||||
i: cython.int = 0
|
||||
while desc.profiles[i].profile != lib.AV_PROFILE_UNKNOWN:
|
||||
ret.append(desc.profiles[i].name)
|
||||
i += 1
|
||||
|
||||
|
|
@ -507,8 +564,8 @@ cdef class CodecContext:
|
|||
# Profiles are always listed in the codec descriptor, but not necessarily in
|
||||
# the codec itself. So use the descriptor here.
|
||||
desc = self.codec.desc
|
||||
cdef int i = 0
|
||||
while desc.profiles[i].profile != lib.AV_PROFILE_UNKNOWN:
|
||||
i: cython.int = 0
|
||||
while desc.profiles[i].profile != lib.AV_PROFILE_UNKNOWN:
|
||||
if desc.profiles[i].profile == self.ptr.profile:
|
||||
return desc.profiles[i].name
|
||||
i += 1
|
||||
|
|
@ -521,7 +578,7 @@ cdef class CodecContext:
|
|||
# Profiles are always listed in the codec descriptor, but not necessarily in
|
||||
# the codec itself. So use the descriptor here.
|
||||
desc = self.codec.desc
|
||||
cdef int i = 0
|
||||
i: cython.int = 0
|
||||
while desc.profiles[i].profile != lib.AV_PROFILE_UNKNOWN:
|
||||
if desc.profiles[i].name == value:
|
||||
self.ptr.profile = desc.profiles[i].profile
|
||||
|
|
@ -532,33 +589,52 @@ cdef class CodecContext:
|
|||
def time_base(self):
|
||||
if self.is_decoder:
|
||||
raise RuntimeError("Cannot access 'time_base' as a decoder")
|
||||
return avrational_to_fraction(&self.ptr.time_base)
|
||||
return avrational_to_fraction(cython.address(self.ptr.time_base))
|
||||
|
||||
@time_base.setter
|
||||
def time_base(self, value):
|
||||
if self.is_decoder:
|
||||
raise RuntimeError("Cannot access 'time_base' as a decoder")
|
||||
to_avrational(value, &self.ptr.time_base)
|
||||
to_avrational(value, cython.address(self.ptr.time_base))
|
||||
|
||||
@property
|
||||
def codec_tag(self):
|
||||
return self.ptr.codec_tag.to_bytes(4, byteorder="little", signed=False).decode(
|
||||
encoding="ascii")
|
||||
encoding="ascii"
|
||||
)
|
||||
|
||||
@codec_tag.setter
|
||||
def codec_tag(self, value):
|
||||
if isinstance(value, str) and len(value) == 4:
|
||||
self.ptr.codec_tag = int.from_bytes(value.encode(encoding="ascii"),
|
||||
byteorder="little", signed=False)
|
||||
self.ptr.codec_tag = int.from_bytes(
|
||||
value.encode(encoding="ascii"), byteorder="little", signed=False
|
||||
)
|
||||
else:
|
||||
raise ValueError("Codec tag should be a 4 character string.")
|
||||
|
||||
@property
|
||||
@cython.cdivision(True)
|
||||
def global_quality(self):
|
||||
"""Global quality for codecs which cannot change it per frame.
|
||||
|
||||
Stored internally in lambda units; this property converts to/from
|
||||
QP units using ``FF_QP2LAMBDA``.
|
||||
|
||||
Wraps :ffmpeg:`AVCodecContext.global_quality`.
|
||||
|
||||
"""
|
||||
return self.ptr.global_quality // lib.FF_QP2LAMBDA
|
||||
|
||||
@global_quality.setter
|
||||
def global_quality(self, value: cython.int):
|
||||
self.ptr.global_quality = value * lib.FF_QP2LAMBDA
|
||||
|
||||
@property
|
||||
def bit_rate(self):
|
||||
return self.ptr.bit_rate if self.ptr.bit_rate > 0 else None
|
||||
|
||||
@bit_rate.setter
|
||||
def bit_rate(self, int value):
|
||||
def bit_rate(self, value: cython.int):
|
||||
self.ptr.bit_rate = value
|
||||
|
||||
@property
|
||||
|
|
@ -573,7 +649,7 @@ cdef class CodecContext:
|
|||
self.ptr.bit_rate_tolerance
|
||||
|
||||
@bit_rate_tolerance.setter
|
||||
def bit_rate_tolerance(self, int value):
|
||||
def bit_rate_tolerance(self, value: cython.int):
|
||||
self.ptr.bit_rate_tolerance = value
|
||||
|
||||
@property
|
||||
|
|
@ -586,7 +662,7 @@ cdef class CodecContext:
|
|||
return self.ptr.thread_count
|
||||
|
||||
@thread_count.setter
|
||||
def thread_count(self, int value):
|
||||
def thread_count(self, value: cython.int):
|
||||
if self.is_open:
|
||||
raise RuntimeError("Cannot change thread_count after codec is open.")
|
||||
self.ptr.thread_count = value
|
||||
|
|
@ -62,6 +62,7 @@ class CodecContext:
|
|||
extradata: bytes | None
|
||||
time_base: Fraction
|
||||
codec_tag: str
|
||||
global_quality: int
|
||||
bit_rate: int | None
|
||||
bit_rate_tolerance: int
|
||||
thread_count: int
|
||||
|
|
|
|||
BIN
venv/lib/python3.12/site-packages/av/codec/hwaccel.abi3.so
Executable file
BIN
venv/lib/python3.12/site-packages/av/codec/hwaccel.abi3.so
Executable file
Binary file not shown.
Binary file not shown.
|
|
@ -5,10 +5,10 @@ from av.codec.codec cimport Codec
|
|||
|
||||
cdef class HWConfig:
|
||||
cdef object __weakref__
|
||||
cdef lib.AVCodecHWConfig *ptr
|
||||
cdef void _init(self, lib.AVCodecHWConfig *ptr)
|
||||
cdef const lib.AVCodecHWConfig *ptr
|
||||
cdef void _init(self, const lib.AVCodecHWConfig *ptr)
|
||||
|
||||
cdef HWConfig wrap_hwconfig(lib.AVCodecHWConfig *ptr)
|
||||
cdef HWConfig wrap_hwconfig(const lib.AVCodecHWConfig *ptr)
|
||||
|
||||
cdef class HWAccel:
|
||||
cdef int _device_type
|
||||
|
|
@ -16,6 +16,8 @@ cdef class HWAccel:
|
|||
cdef readonly Codec codec
|
||||
cdef readonly HWConfig config
|
||||
cdef lib.AVBufferRef *ptr
|
||||
cdef readonly int device_id
|
||||
cdef readonly bint is_hw_owned
|
||||
cdef public bint allow_software_fallback
|
||||
cdef public dict options
|
||||
cdef public int flags
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import weakref
|
||||
from enum import IntEnum
|
||||
|
||||
cimport libav as lib
|
||||
|
||||
from av.codec.codec cimport Codec
|
||||
from av.dictionary cimport _Dictionary
|
||||
from av.error cimport err_check
|
||||
from av.video.format cimport get_video_format
|
||||
|
||||
from av.dictionary import Dictionary
|
||||
import cython
|
||||
import cython.cimports.libav as lib
|
||||
from cython.cimports.av.codec.codec import Codec
|
||||
from cython.cimports.av.dictionary import Dictionary
|
||||
from cython.cimports.av.error import err_check
|
||||
from cython.cimports.av.video.format import get_video_format
|
||||
|
||||
|
||||
class HWDeviceType(IntEnum):
|
||||
|
|
@ -29,34 +27,44 @@ class HWDeviceType(IntEnum):
|
|||
ohcodec = 14
|
||||
# TODO: When ffmpeg major is changed, check this enum.
|
||||
|
||||
|
||||
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_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()
|
||||
cdef object _singletons = weakref.WeakValueDictionary()
|
||||
_cinit_sentinel = cython.declare(object, object())
|
||||
_singletons = cython.declare(object, weakref.WeakValueDictionary())
|
||||
|
||||
cdef HWConfig wrap_hwconfig(lib.AVCodecHWConfig *ptr):
|
||||
|
||||
@cython.cfunc
|
||||
def wrap_hwconfig(ptr: cython.pointer[cython.const[lib.AVCodecHWConfig]]) -> HWConfig:
|
||||
try:
|
||||
return _singletons[<int>ptr]
|
||||
return _singletons[cython.cast(cython.Py_ssize_t, ptr)]
|
||||
except KeyError:
|
||||
pass
|
||||
cdef HWConfig config = HWConfig(_cinit_sentinel)
|
||||
config: HWConfig = HWConfig(_cinit_sentinel)
|
||||
config._init(ptr)
|
||||
_singletons[<int>ptr] = config
|
||||
_singletons[cython.cast(cython.Py_ssize_t, ptr)] = config
|
||||
return config
|
||||
|
||||
|
||||
cdef class HWConfig:
|
||||
@cython.final
|
||||
@cython.cclass
|
||||
class HWConfig:
|
||||
def __init__(self, sentinel):
|
||||
if sentinel is not _cinit_sentinel:
|
||||
raise RuntimeError("Cannot instantiate CodecContext")
|
||||
|
||||
cdef void _init(self, lib.AVCodecHWConfig *ptr):
|
||||
@cython.cfunc
|
||||
def _init(
|
||||
self, ptr: cython.pointer[cython.const[lib.AVCodecHWConfig]]
|
||||
) -> cython.void:
|
||||
self.ptr = ptr
|
||||
|
||||
def __repr__(self):
|
||||
|
|
@ -64,7 +72,7 @@ cdef class HWConfig:
|
|||
f"<av.{self.__class__.__name__} "
|
||||
f"device_type={lib.av_hwdevice_get_type_name(self.device_type)} "
|
||||
f"format={self.format.name if self.format else None} "
|
||||
f"is_supported={self.is_supported} at 0x{<int>self.ptr:x}>"
|
||||
f"is_supported={self.is_supported} at 0x{cython.cast(cython.Py_ssize_t, self.ptr):x}>"
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
@ -84,21 +92,30 @@ cdef class HWConfig:
|
|||
return bool(self.ptr.methods & lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
|
||||
|
||||
|
||||
cpdef hwdevices_available():
|
||||
result = []
|
||||
|
||||
cdef lib.AVHWDeviceType x = lib.AV_HWDEVICE_TYPE_NONE
|
||||
@cython.ccall
|
||||
def hwdevices_available():
|
||||
result: list = []
|
||||
x: lib.AVHWDeviceType = 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, device=None, allow_software_fallback=True, options=None, flags=None):
|
||||
@cython.final
|
||||
@cython.cclass
|
||||
class HWAccel:
|
||||
def __init__(
|
||||
self,
|
||||
device_type,
|
||||
device=None,
|
||||
allow_software_fallback=True,
|
||||
options=None,
|
||||
flags=None,
|
||||
is_hw_owned=False,
|
||||
):
|
||||
if isinstance(device_type, HWDeviceType):
|
||||
self._device_type = device_type
|
||||
elif isinstance(device_type, str):
|
||||
|
|
@ -108,39 +125,50 @@ cdef class HWAccel:
|
|||
else:
|
||||
raise ValueError("Unknown type for device_type")
|
||||
|
||||
self._device = device
|
||||
self.is_hw_owned = is_hw_owned
|
||||
self.device_id = 0
|
||||
if self._device_type == HWDeviceType.cuda and device:
|
||||
self.device_id = int(device)
|
||||
|
||||
self._device = None if device is None else f"{device}"
|
||||
self.allow_software_fallback = allow_software_fallback
|
||||
|
||||
self.options = {} if not options else dict(options)
|
||||
if self._device_type == HWDeviceType.cuda and self.is_hw_owned:
|
||||
self.options.setdefault("primary_ctx", "1")
|
||||
self.flags = 0 if not flags else flags
|
||||
self.ptr = NULL
|
||||
self.ptr = cython.NULL
|
||||
self.config = None
|
||||
|
||||
def _initialize_hw_context(self, Codec codec not None):
|
||||
cdef HWConfig config
|
||||
def _initialize_hw_context(self, codec: Codec):
|
||||
config: HWConfig
|
||||
for config in codec.hardware_configs:
|
||||
if not (config.ptr.methods & lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX):
|
||||
continue
|
||||
if self._device_type and config.device_type != self._device_type:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
else: # nobreak
|
||||
raise NotImplementedError(f"No supported hardware config for {codec}")
|
||||
|
||||
self.config = config
|
||||
|
||||
cdef char *c_device = NULL
|
||||
c_device: cython.p_char = cython.NULL
|
||||
if self._device:
|
||||
device_bytes = self._device.encode()
|
||||
c_device = device_bytes
|
||||
cdef _Dictionary c_options = Dictionary(self.options)
|
||||
c_options: Dictionary = Dictionary(self.options)
|
||||
|
||||
err_check(
|
||||
lib.av_hwdevice_ctx_create(
|
||||
&self.ptr, config.ptr.device_type, c_device, c_options.ptr, self.flags
|
||||
cython.address(self.ptr),
|
||||
config.ptr.device_type,
|
||||
c_device,
|
||||
c_options.ptr,
|
||||
self.flags,
|
||||
)
|
||||
)
|
||||
|
||||
def create(self, Codec codec not None):
|
||||
def create(self, codec: Codec) -> HWAccel:
|
||||
"""Create a new hardware accelerator context with the given codec"""
|
||||
if self.ptr:
|
||||
raise RuntimeError("Hardware context already initialized")
|
||||
|
|
@ -149,11 +177,12 @@ cdef class HWAccel:
|
|||
device_type=self._device_type,
|
||||
device=self._device,
|
||||
allow_software_fallback=self.allow_software_fallback,
|
||||
options=self.options
|
||||
options=self.options,
|
||||
is_hw_owned=self.is_hw_owned,
|
||||
)
|
||||
ret._initialize_hw_context(codec)
|
||||
return ret
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.ptr:
|
||||
lib.av_buffer_unref(&self.ptr)
|
||||
lib.av_buffer_unref(cython.address(self.ptr))
|
||||
|
|
@ -37,13 +37,20 @@ class HWConfig:
|
|||
def is_supported(self) -> bool: ...
|
||||
|
||||
class HWAccel:
|
||||
options: dict[str, object]
|
||||
|
||||
@property
|
||||
def is_hw_owned(self) -> bool: ...
|
||||
@property
|
||||
def device_id(self) -> int: ...
|
||||
def __init__(
|
||||
self,
|
||||
device_type: str | HWDeviceType,
|
||||
device: str | None = None,
|
||||
device: str | int | None = None,
|
||||
allow_software_fallback: bool = False,
|
||||
options: dict[str, object] | None = None,
|
||||
flags: int | None = None,
|
||||
is_hw_owned: bool = False,
|
||||
) -> None: ...
|
||||
def create(self, codec: Codec) -> HWAccel: ...
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue