add read me
This commit is contained in:
11
venv/lib/python3.12/site-packages/av/codec/__init__.py
Normal file
11
venv/lib/python3.12/site-packages/av/codec/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from .codec import Capabilities, Codec, Properties, codec_descriptor, codecs_available
|
||||
from .context import CodecContext
|
||||
|
||||
__all__ = (
|
||||
"Capabilities",
|
||||
"Codec",
|
||||
"Properties",
|
||||
"codec_descriptor",
|
||||
"codecs_available",
|
||||
"CodecContext",
|
||||
)
|
||||
Binary file not shown.
BIN
venv/lib/python3.12/site-packages/av/codec/codec.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/av/codec/codec.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
15
venv/lib/python3.12/site-packages/av/codec/codec.pxd
Normal file
15
venv/lib/python3.12/site-packages/av/codec/codec.pxd
Normal file
@@ -0,0 +1,15 @@
|
||||
cimport libav as lib
|
||||
|
||||
|
||||
cdef class Codec:
|
||||
|
||||
cdef const lib.AVCodec *ptr
|
||||
cdef const lib.AVCodecDescriptor *desc
|
||||
cdef readonly bint is_encoder
|
||||
|
||||
cdef tuple _hardware_configs
|
||||
|
||||
cdef _init(self, name=?)
|
||||
|
||||
|
||||
cdef Codec wrap_codec(const lib.AVCodec *ptr)
|
||||
115
venv/lib/python3.12/site-packages/av/codec/codec.pyi
Normal file
115
venv/lib/python3.12/site-packages/av/codec/codec.pyi
Normal file
@@ -0,0 +1,115 @@
|
||||
from enum import Flag, IntEnum
|
||||
from fractions import Fraction
|
||||
from typing import ClassVar, Literal, 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
|
||||
|
||||
from .context import CodecContext
|
||||
|
||||
class Properties(Flag):
|
||||
NONE = cast(ClassVar[Properties], ...)
|
||||
INTRA_ONLY = cast(ClassVar[Properties], ...)
|
||||
LOSSY = cast(ClassVar[Properties], ...)
|
||||
LOSSLESS = cast(ClassVar[Properties], ...)
|
||||
REORDER = cast(ClassVar[Properties], ...)
|
||||
BITMAP_SUB = cast(ClassVar[Properties], ...)
|
||||
TEXT_SUB = cast(ClassVar[Properties], ...)
|
||||
|
||||
class Capabilities(IntEnum):
|
||||
none = cast(int, ...)
|
||||
draw_horiz_band = cast(int, ...)
|
||||
dr1 = cast(int, ...)
|
||||
hwaccel = cast(int, ...)
|
||||
delay = cast(int, ...)
|
||||
small_last_frame = cast(int, ...)
|
||||
hwaccel_vdpau = cast(int, ...)
|
||||
subframes = cast(int, ...)
|
||||
experimental = cast(int, ...)
|
||||
channel_conf = cast(int, ...)
|
||||
neg_linesizes = cast(int, ...)
|
||||
frame_threads = cast(int, ...)
|
||||
slice_threads = cast(int, ...)
|
||||
param_change = cast(int, ...)
|
||||
auto_threads = cast(int, ...)
|
||||
variable_frame_size = cast(int, ...)
|
||||
avoid_probing = cast(int, ...)
|
||||
hardware = cast(int, ...)
|
||||
hybrid = cast(int, ...)
|
||||
encoder_reordered_opaque = cast(int, ...)
|
||||
encoder_flush = cast(int, ...)
|
||||
encoder_recon_frame = cast(int, ...)
|
||||
|
||||
class UnknownCodecError(ValueError): ...
|
||||
|
||||
class Codec:
|
||||
@property
|
||||
def is_encoder(self) -> bool: ...
|
||||
@property
|
||||
def is_decoder(self) -> bool: ...
|
||||
@property
|
||||
def mode(self) -> Literal["r", "w"]: ...
|
||||
descriptor: Descriptor
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@property
|
||||
def canonical_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
|
||||
|
||||
@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: ...
|
||||
@property
|
||||
def capabilities(self) -> int: ...
|
||||
@property
|
||||
def experimental(self) -> bool: ...
|
||||
@property
|
||||
def delay(self) -> bool: ...
|
||||
def __init__(self, name: str, mode: Literal["r", "w"] = "r") -> None: ...
|
||||
@overload
|
||||
def create(self, kind: Literal["video"]) -> VideoCodecContext: ...
|
||||
@overload
|
||||
def create(self, kind: Literal["audio"]) -> AudioCodecContext: ...
|
||||
@overload
|
||||
def create(self, kind: Literal["subtitle"]) -> SubtitleCodecContext: ...
|
||||
@overload
|
||||
def create(self, kind: None = None) -> CodecContext: ...
|
||||
@overload
|
||||
def create(
|
||||
self, kind: Literal["video", "audio", "subtitle"] | None = None
|
||||
) -> (
|
||||
VideoCodecContext | AudioCodecContext | SubtitleCodecContext | CodecContext
|
||||
): ...
|
||||
|
||||
class codec_descriptor:
|
||||
name: str
|
||||
options: tuple[int, ...]
|
||||
|
||||
codecs_available: set[str]
|
||||
|
||||
def dump_codecs() -> None: ...
|
||||
def dump_hwconfigs() -> None: ...
|
||||
389
venv/lib/python3.12/site-packages/av/codec/codec.pyx
Normal file
389
venv/lib/python3.12/site-packages/av/codec/codec.pyx
Normal file
@@ -0,0 +1,389 @@
|
||||
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 get_video_format
|
||||
|
||||
from enum import Flag, IntEnum
|
||||
|
||||
|
||||
cdef object _cinit_sentinel = object()
|
||||
|
||||
cdef Codec wrap_codec(const lib.AVCodec *ptr):
|
||||
cdef 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
|
||||
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
|
||||
|
||||
|
||||
class Capabilities(IntEnum):
|
||||
none = 0
|
||||
draw_horiz_band = lib.AV_CODEC_CAP_DRAW_HORIZ_BAND
|
||||
dr1 = lib.AV_CODEC_CAP_DR1
|
||||
hwaccel = 1 << 4
|
||||
delay = lib.AV_CODEC_CAP_DELAY
|
||||
small_last_frame = lib.AV_CODEC_CAP_SMALL_LAST_FRAME
|
||||
hwaccel_vdpau = 1 << 7
|
||||
experimental = lib.AV_CODEC_CAP_EXPERIMENTAL
|
||||
channel_conf = lib.AV_CODEC_CAP_CHANNEL_CONF
|
||||
neg_linesizes = 1 << 11
|
||||
frame_threads = lib.AV_CODEC_CAP_FRAME_THREADS
|
||||
slice_threads = lib.AV_CODEC_CAP_SLICE_THREADS
|
||||
param_change = lib.AV_CODEC_CAP_PARAM_CHANGE
|
||||
auto_threads = lib.AV_CODEC_CAP_OTHER_THREADS
|
||||
variable_frame_size = lib.AV_CODEC_CAP_VARIABLE_FRAME_SIZE
|
||||
avoid_probing = lib.AV_CODEC_CAP_AVOID_PROBING
|
||||
hardware = lib.AV_CODEC_CAP_HARDWARE
|
||||
hybrid = lib.AV_CODEC_CAP_HYBRID
|
||||
encoder_reordered_opaque = 1 << 20
|
||||
encoder_flush = 1 << 21
|
||||
encoder_recon_frame = 1 << 22
|
||||
|
||||
|
||||
class UnknownCodecError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
cdef class Codec:
|
||||
"""Codec(name, mode='r')
|
||||
|
||||
:param str name: The codec name.
|
||||
:param str mode: ``'r'`` for decoding or ``'w'`` for encoding.
|
||||
|
||||
This object exposes information about an available codec, and an avenue to
|
||||
create a :class:`.CodecContext` to encode/decode directly.
|
||||
|
||||
::
|
||||
|
||||
>>> codec = Codec('mpeg4', 'r')
|
||||
>>> codec.name
|
||||
'mpeg4'
|
||||
>>> codec.type
|
||||
'video'
|
||||
>>> codec.is_encoder
|
||||
False
|
||||
|
||||
"""
|
||||
|
||||
def __cinit__(self, name, mode="r"):
|
||||
if name is _cinit_sentinel:
|
||||
return
|
||||
|
||||
if mode == "w":
|
||||
self.ptr = lib.avcodec_find_encoder_by_name(name)
|
||||
if not self.ptr:
|
||||
self.desc = lib.avcodec_descriptor_get_by_name(name)
|
||||
if self.desc:
|
||||
self.ptr = lib.avcodec_find_encoder(self.desc.id)
|
||||
|
||||
elif mode == "r":
|
||||
self.ptr = lib.avcodec_find_decoder_by_name(name)
|
||||
if not self.ptr:
|
||||
self.desc = lib.avcodec_descriptor_get_by_name(name)
|
||||
if self.desc:
|
||||
self.ptr = lib.avcodec_find_decoder(self.desc.id)
|
||||
|
||||
else:
|
||||
raise ValueError('Invalid mode; must be "r" or "w".', mode)
|
||||
|
||||
self._init(name)
|
||||
|
||||
# Sanity check.
|
||||
if (mode == "w") != self.is_encoder:
|
||||
raise RuntimeError("Found codec does not match mode.", name, mode)
|
||||
|
||||
cdef _init(self, name=None):
|
||||
if not self.ptr:
|
||||
raise UnknownCodecError(name)
|
||||
|
||||
if not self.desc:
|
||||
self.desc = lib.avcodec_descriptor_get(self.ptr.id)
|
||||
if not self.desc:
|
||||
raise RuntimeError("No codec descriptor for %r." % name)
|
||||
|
||||
self.is_encoder = lib.av_codec_is_encoder(self.ptr)
|
||||
|
||||
# Sanity check.
|
||||
if self.is_encoder and lib.av_codec_is_decoder(self.ptr):
|
||||
raise RuntimeError("%s is both encoder and decoder.")
|
||||
|
||||
def __repr__(self):
|
||||
mode = self.mode
|
||||
return f"<av.{self.__class__.__name__} {self.name} {mode=}>"
|
||||
|
||||
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
|
||||
def mode(self):
|
||||
return "w" if self.is_encoder else "r"
|
||||
|
||||
@property
|
||||
def is_decoder(self):
|
||||
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 ""
|
||||
|
||||
@property
|
||||
def canonical_name(self):
|
||||
"""
|
||||
Returns the name of the codec, not a specific encoder.
|
||||
"""
|
||||
return lib.avcodec_get_name(self.ptr.id)
|
||||
|
||||
@property
|
||||
def long_name(self): return self.ptr.long_name or ""
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""
|
||||
The media type of this codec.
|
||||
|
||||
E.g: ``'audio'``, ``'video'``, ``'subtitle'``.
|
||||
|
||||
"""
|
||||
return lib.av_get_media_type_string(self.ptr.type)
|
||||
|
||||
@property
|
||||
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:
|
||||
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
|
||||
|
||||
@property
|
||||
def audio_rates(self):
|
||||
"""A list of supported audio sample rates (``int``), or ``None``."""
|
||||
if not self.ptr.supported_samplerates:
|
||||
return
|
||||
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
while self.ptr.supported_samplerates[i]:
|
||||
ret.append(self.ptr.supported_samplerates[i])
|
||||
i += 1
|
||||
return ret
|
||||
|
||||
@property
|
||||
def video_formats(self):
|
||||
"""A list of supported :class:`.VideoFormat`, or ``None``."""
|
||||
if not self.ptr.pix_fmts:
|
||||
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
|
||||
|
||||
@property
|
||||
def audio_formats(self):
|
||||
"""A list of supported :class:`.AudioFormat`, or ``None``."""
|
||||
if not self.ptr.sample_fmts:
|
||||
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
|
||||
|
||||
@property
|
||||
def hardware_configs(self):
|
||||
if self._hardware_configs:
|
||||
return self._hardware_configs
|
||||
ret = []
|
||||
cdef int i = 0
|
||||
cdef const lib.AVCodecHWConfig *ptr
|
||||
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
|
||||
|
||||
@property
|
||||
def properties(self):
|
||||
return self.desc.props
|
||||
|
||||
@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)
|
||||
|
||||
@property
|
||||
def capabilities(self):
|
||||
"""
|
||||
Get the capabilities bitmask of the codec.
|
||||
|
||||
This method returns an integer representing the codec capabilities bitmask,
|
||||
which can be used to check specific codec features by performing bitwise
|
||||
operations with the Capabilities enum values.
|
||||
|
||||
:example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from av.codec import Codec, Capabilities
|
||||
|
||||
codec = Codec("h264", "w")
|
||||
|
||||
# Check if the codec can be fed a final frame with a smaller size.
|
||||
# This can be used to prevent truncation of the last audio samples.
|
||||
small_last_frame = bool(codec.capabilities & Capabilities.small_last_frame)
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
return self.ptr.capabilities
|
||||
|
||||
@property
|
||||
def experimental(self):
|
||||
"""
|
||||
Check if codec is experimental and is thus avoided in favor of non experimental encoders.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return bool(self.ptr.capabilities & lib.AV_CODEC_CAP_EXPERIMENTAL)
|
||||
|
||||
@property
|
||||
def delay(self):
|
||||
"""
|
||||
If true, encoder or decoder requires flushing with `None` at the end in order to give the complete and correct output.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
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
|
||||
while True:
|
||||
ptr = lib.av_codec_iterate(&opaque)
|
||||
if ptr:
|
||||
names.add(ptr.name)
|
||||
else:
|
||||
break
|
||||
return names
|
||||
|
||||
|
||||
codecs_available = get_codec_names()
|
||||
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. = Lossy compression
|
||||
.....S = Lossless compression
|
||||
------"""
|
||||
)
|
||||
|
||||
for name in sorted(codecs_available):
|
||||
try:
|
||||
e_codec = Codec(name, "w")
|
||||
except ValueError:
|
||||
e_codec = None
|
||||
|
||||
try:
|
||||
d_codec = Codec(name, "r")
|
||||
except ValueError:
|
||||
d_codec = None
|
||||
|
||||
# TODO: Assert these always have the same properties.
|
||||
codec = e_codec or d_codec
|
||||
|
||||
try:
|
||||
print(
|
||||
" %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.lossy],
|
||||
".S"[codec.lossless],
|
||||
codec.name,
|
||||
codec.long_name,
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"...... {codec.name:<18} ERROR: {e}")
|
||||
|
||||
def dump_hwconfigs():
|
||||
print("Hardware configs:")
|
||||
for name in sorted(codecs_available):
|
||||
try:
|
||||
codec = Codec(name, "r")
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
configs = codec.hardware_configs
|
||||
if not configs:
|
||||
continue
|
||||
|
||||
print(" ", codec.name)
|
||||
for config in configs:
|
||||
print(" ", config)
|
||||
Binary file not shown.
64
venv/lib/python3.12/site-packages/av/codec/context.pxd
Normal file
64
venv/lib/python3.12/site-packages/av/codec/context.pxd
Normal file
@@ -0,0 +1,64 @@
|
||||
cimport libav as lib
|
||||
from libc.stdint cimport int64_t
|
||||
|
||||
from av.bytesource cimport ByteSource
|
||||
from av.codec.codec cimport Codec
|
||||
from av.codec.hwaccel cimport HWAccel
|
||||
from av.frame cimport Frame
|
||||
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
|
||||
|
||||
cdef lib.AVCodecParserContext *parser
|
||||
cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec, HWAccel hwaccel)
|
||||
|
||||
# Public API.
|
||||
cdef readonly bint is_open
|
||||
cdef readonly Codec codec
|
||||
cdef readonly HWAccel hwaccel
|
||||
cdef public dict options
|
||||
cpdef open(self, bint strict=?)
|
||||
|
||||
# Wraps both versions of the transcode API, returning lists.
|
||||
cpdef encode(self, Frame frame=?)
|
||||
cpdef decode(self, Packet packet=?)
|
||||
cpdef flush_buffers(self)
|
||||
|
||||
# Used by hardware-accelerated decode.
|
||||
cdef HWAccel hwaccel_ctx
|
||||
|
||||
# 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 _setup_encoded_packet(self, Packet)
|
||||
cdef _setup_decoded_frame(self, Frame, Packet)
|
||||
|
||||
# Implemented by base for the generic send/recv API.
|
||||
# Note that the user cannot send without receiving. This is because
|
||||
# `_prepare_frames_for_encode` may expand a frame into multiple (e.g. when
|
||||
# resampling audio to a higher rate but with fixed size frames), and the
|
||||
# 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)
|
||||
|
||||
# Implemented by children for the generic send/recv API, so we have the
|
||||
# correct subclass of Frame.
|
||||
cdef Frame _next_frame
|
||||
cdef Frame _alloc_next_frame(self)
|
||||
|
||||
cdef CodecContext wrap_codec_context(lib.AVCodecContext*, const lib.AVCodec*, HWAccel hwaccel)
|
||||
117
venv/lib/python3.12/site-packages/av/codec/context.pyi
Normal file
117
venv/lib/python3.12/site-packages/av/codec/context.pyi
Normal file
@@ -0,0 +1,117 @@
|
||||
from enum import Flag, IntEnum
|
||||
from fractions import Fraction
|
||||
from typing import ClassVar, Literal, cast, overload
|
||||
|
||||
from av.audio import _AudioCodecName
|
||||
from av.audio.codeccontext import AudioCodecContext
|
||||
from av.packet import Packet
|
||||
from av.video import _VideoCodecName
|
||||
from av.video.codeccontext import VideoCodecContext
|
||||
|
||||
from .codec import Codec
|
||||
from .hwaccel import HWAccel
|
||||
|
||||
class ThreadType(Flag):
|
||||
NONE = cast(ClassVar[ThreadType], ...)
|
||||
FRAME = cast(ClassVar[ThreadType], ...)
|
||||
SLICE = cast(ClassVar[ThreadType], ...)
|
||||
AUTO = cast(ClassVar[ThreadType], ...)
|
||||
def __get__(self, i: object | None, owner: type | None = None) -> ThreadType: ...
|
||||
def __set__(self, instance: object, value: int | str | ThreadType) -> None: ...
|
||||
|
||||
class Flags(IntEnum):
|
||||
unaligned = cast(int, ...)
|
||||
qscale = cast(int, ...)
|
||||
four_mv = cast(int, ...)
|
||||
output_corrupt = cast(int, ...)
|
||||
qpel = cast(int, ...)
|
||||
recon_frame = cast(int, ...)
|
||||
copy_opaque = cast(int, ...)
|
||||
frame_duration = cast(int, ...)
|
||||
pass1 = cast(int, ...)
|
||||
pass2 = cast(int, ...)
|
||||
loop_filter = cast(int, ...)
|
||||
gray = cast(int, ...)
|
||||
psnr = cast(int, ...)
|
||||
interlaced_dct = cast(int, ...)
|
||||
low_delay = cast(int, ...)
|
||||
global_header = cast(int, ...)
|
||||
bitexact = cast(int, ...)
|
||||
ac_pred = cast(int, ...)
|
||||
interlaced_me = cast(int, ...)
|
||||
closed_gop = cast(int, ...)
|
||||
|
||||
class Flags2(IntEnum):
|
||||
fast = cast(int, ...)
|
||||
no_output = cast(int, ...)
|
||||
local_header = cast(int, ...)
|
||||
chunks = cast(int, ...)
|
||||
ignore_crop = cast(int, ...)
|
||||
show_all = cast(int, ...)
|
||||
export_mvs = cast(int, ...)
|
||||
skip_manual = cast(int, ...)
|
||||
ro_flush_noop = cast(int, ...)
|
||||
|
||||
class CodecContext:
|
||||
name: str
|
||||
type: Literal["video", "audio", "data", "subtitle", "attachment"]
|
||||
options: dict[str, str]
|
||||
profile: str | None
|
||||
@property
|
||||
def profiles(self) -> list[str]: ...
|
||||
extradata: bytes | None
|
||||
time_base: Fraction
|
||||
codec_tag: str
|
||||
bit_rate: int | None
|
||||
bit_rate_tolerance: int
|
||||
thread_count: int
|
||||
thread_type: ThreadType
|
||||
skip_frame: Literal[
|
||||
"NONE", "DEFAULT", "NONREF", "BIDIR", "NONINTRA", "NONKEY", "ALL"
|
||||
]
|
||||
flags: int
|
||||
qscale: bool
|
||||
copy_opaque: bool
|
||||
flags2: int
|
||||
@property
|
||||
def is_open(self) -> bool: ...
|
||||
@property
|
||||
def is_encoder(self) -> bool: ...
|
||||
@property
|
||||
def is_decoder(self) -> bool: ...
|
||||
@property
|
||||
def codec(self) -> Codec: ...
|
||||
@property
|
||||
def max_bit_rate(self) -> int | None: ...
|
||||
@property
|
||||
def delay(self) -> bool: ...
|
||||
@property
|
||||
def extradata_size(self) -> int: ...
|
||||
@property
|
||||
def is_hwaccel(self) -> bool: ...
|
||||
def open(self, strict: bool = True) -> None: ...
|
||||
@overload
|
||||
@staticmethod
|
||||
def create(
|
||||
codec: _AudioCodecName,
|
||||
mode: Literal["r", "w"] | None = None,
|
||||
hwaccel: HWAccel | None = None,
|
||||
) -> AudioCodecContext: ...
|
||||
@overload
|
||||
@staticmethod
|
||||
def create(
|
||||
codec: _VideoCodecName,
|
||||
mode: Literal["r", "w"] | None = None,
|
||||
hwaccel: HWAccel | None = None,
|
||||
) -> VideoCodecContext: ...
|
||||
@overload
|
||||
@staticmethod
|
||||
def create(
|
||||
codec: str | Codec,
|
||||
mode: Literal["r", "w"] | None = None,
|
||||
hwaccel: HWAccel | None = None,
|
||||
) -> CodecContext: ...
|
||||
def parse(
|
||||
self, raw_input: bytes | bytearray | memoryview | None = None
|
||||
) -> list[Packet]: ...
|
||||
def flush_buffers(self) -> None: ...
|
||||
671
venv/lib/python3.12/site-packages/av/codec/context.pyx
Normal file
671
venv/lib/python3.12/site-packages/av/codec/context.pyx
Normal file
@@ -0,0 +1,671 @@
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
py_ctx._init(c_ctx, c_codec, hwaccel)
|
||||
|
||||
return py_ctx
|
||||
|
||||
|
||||
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
|
||||
|
||||
class Flags(IntEnum):
|
||||
unaligned = lib.AV_CODEC_FLAG_UNALIGNED
|
||||
qscale = lib.AV_CODEC_FLAG_QSCALE
|
||||
four_mv = lib.AV_CODEC_FLAG_4MV
|
||||
output_corrupt = lib.AV_CODEC_FLAG_OUTPUT_CORRUPT
|
||||
qpel = lib.AV_CODEC_FLAG_QPEL
|
||||
recon_frame = lib.AV_CODEC_FLAG_RECON_FRAME
|
||||
copy_opaque = lib.AV_CODEC_FLAG_COPY_OPAQUE
|
||||
frame_duration = lib.AV_CODEC_FLAG_FRAME_DURATION
|
||||
pass1 = lib.AV_CODEC_FLAG_PASS1
|
||||
pass2 = lib.AV_CODEC_FLAG_PASS2
|
||||
loop_filter = lib.AV_CODEC_FLAG_LOOP_FILTER
|
||||
gray = lib.AV_CODEC_FLAG_GRAY
|
||||
psnr = lib.AV_CODEC_FLAG_PSNR
|
||||
interlaced_dct = lib.AV_CODEC_FLAG_INTERLACED_DCT
|
||||
low_delay = lib.AV_CODEC_FLAG_LOW_DELAY
|
||||
global_header = lib.AV_CODEC_FLAG_GLOBAL_HEADER
|
||||
bitexact = lib.AV_CODEC_FLAG_BITEXACT
|
||||
ac_pred = lib.AV_CODEC_FLAG_AC_PRED
|
||||
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
|
||||
local_header = lib.AV_CODEC_FLAG2_LOCAL_HEADER
|
||||
chunks = lib.AV_CODEC_FLAG2_CHUNKS
|
||||
ignore_crop = lib.AV_CODEC_FLAG2_IGNORE_CROP
|
||||
show_all = lib.AV_CODEC_FLAG2_SHOW_ALL
|
||||
export_mvs = lib.AV_CODEC_FLAG2_EXPORT_MVS
|
||||
skip_manual = lib.AV_CODEC_FLAG2_SKIP_MANUAL
|
||||
ro_flush_noop = lib.AV_CODEC_FLAG2_RO_FLUSH_NOOP
|
||||
|
||||
|
||||
cdef 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)
|
||||
return wrap_codec_context(c_ctx, cy_codec.ptr, hwaccel)
|
||||
|
||||
def __cinit__(self, sentinel=None, *args, **kwargs):
|
||||
if sentinel is not _cinit_sentinel:
|
||||
raise RuntimeError("Cannot instantiate CodecContext")
|
||||
|
||||
self.options = {}
|
||||
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):
|
||||
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.hwaccel = hwaccel
|
||||
|
||||
# Set reasonable threading defaults.
|
||||
self.ptr.thread_count = 0 # use as many threads as there are CPUs.
|
||||
self.ptr.thread_type = 0x02 # thread within a frame. Does not change the API.
|
||||
|
||||
@property
|
||||
def flags(self):
|
||||
"""
|
||||
Get and set the flags bitmask of CodecContext.
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
return self.ptr.flags
|
||||
|
||||
@flags.setter
|
||||
def flags(self, int value):
|
||||
self.ptr.flags = value
|
||||
|
||||
@property
|
||||
def qscale(self):
|
||||
"""
|
||||
Use fixed qscale.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return bool(self.ptr.flags & lib.AV_CODEC_FLAG_QSCALE)
|
||||
|
||||
@qscale.setter
|
||||
def qscale(self, value):
|
||||
if value:
|
||||
self.ptr.flags |= lib.AV_CODEC_FLAG_QSCALE
|
||||
else:
|
||||
self.ptr.flags &= ~lib.AV_CODEC_FLAG_QSCALE
|
||||
|
||||
@property
|
||||
def copy_opaque(self):
|
||||
return bool(self.ptr.flags & lib.AV_CODEC_FLAG_COPY_OPAQUE)
|
||||
|
||||
@copy_opaque.setter
|
||||
def copy_opaque(self, value):
|
||||
if value:
|
||||
self.ptr.flags |= lib.AV_CODEC_FLAG_COPY_OPAQUE
|
||||
else:
|
||||
self.ptr.flags &= ~lib.AV_CODEC_FLAG_COPY_OPAQUE
|
||||
|
||||
@property
|
||||
def flags2(self):
|
||||
"""
|
||||
Get and set the flags2 bitmask of CodecContext.
|
||||
|
||||
:rtype: int
|
||||
"""
|
||||
return self.ptr.flags2
|
||||
|
||||
@flags2.setter
|
||||
def flags2(self, int value):
|
||||
self.ptr.flags2 = value
|
||||
|
||||
@property
|
||||
def extradata(self):
|
||||
if self.ptr is NULL:
|
||||
return None
|
||||
if self.ptr.extradata_size > 0:
|
||||
return <bytes>(<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)
|
||||
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)
|
||||
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):
|
||||
return self.ptr.extradata_size
|
||||
|
||||
@property
|
||||
def is_encoder(self):
|
||||
if self.ptr is NULL:
|
||||
return False
|
||||
return lib.av_codec_is_encoder(self.ptr.codec)
|
||||
|
||||
@property
|
||||
def is_decoder(self):
|
||||
if self.ptr is NULL:
|
||||
return False
|
||||
return lib.av_codec_is_decoder(self.ptr.codec)
|
||||
|
||||
cpdef open(self, bint strict=True):
|
||||
if self.is_open:
|
||||
if strict:
|
||||
raise ValueError("CodecContext is already open.")
|
||||
return
|
||||
|
||||
cdef _Dictionary options = Dictionary()
|
||||
options.update(self.options or {})
|
||||
|
||||
if not self.ptr.time_base.num and self.is_encoder:
|
||||
if self.type == "video":
|
||||
self.ptr.time_base.num = self.ptr.framerate.den or 1
|
||||
self.ptr.time_base.den = self.ptr.framerate.num or lib.AV_TIME_BASE
|
||||
elif self.type == "audio":
|
||||
self.ptr.time_base.num = 1
|
||||
self.ptr.time_base.den = self.ptr.sample_rate
|
||||
else:
|
||||
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 + ")")
|
||||
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)
|
||||
if self.parser:
|
||||
lib.av_parser_close(self.parser)
|
||||
|
||||
def __repr__(self):
|
||||
_type = self.type or "<notype>"
|
||||
name = self.name or "<nocodec>"
|
||||
return f"<av.{self.__class__.__name__} {_type}/{name} at 0x{id(self):x}>"
|
||||
|
||||
def parse(self, raw_input=None):
|
||||
"""Split up a byte stream into list of :class:`.Packet`.
|
||||
|
||||
This is only effectively splitting up a byte stream, and does no
|
||||
actual interpretation of the data.
|
||||
|
||||
It will return all packets that are fully contained within the given
|
||||
input, and will buffer partial packets until they are complete.
|
||||
|
||||
: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.
|
||||
|
||||
:return: ``list`` of :class:`.Packet` newly available.
|
||||
|
||||
"""
|
||||
|
||||
if not self.parser:
|
||||
self.parser = lib.av_parser_init(self.codec.ptr.id)
|
||||
if not self.parser:
|
||||
raise ValueError(f"No parser for {self.codec.name}")
|
||||
|
||||
cdef ByteSource source = 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
|
||||
|
||||
cdef unsigned char *out_data
|
||||
cdef int out_size
|
||||
cdef int consumed
|
||||
cdef Packet packet = None
|
||||
|
||||
packets = []
|
||||
|
||||
while True:
|
||||
with 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
|
||||
)
|
||||
err_check(consumed)
|
||||
|
||||
if out_size:
|
||||
# We copy the data immediately, as we have yet to figure out
|
||||
# the expected lifetime of the buffer we get back. All of the
|
||||
# examples decode it immediately.
|
||||
#
|
||||
# We've also tried:
|
||||
# packet = Packet()
|
||||
# packet.data = out_data
|
||||
# packet.size = out_size
|
||||
# packet.source = source
|
||||
#
|
||||
# ... but this results in corruption.
|
||||
|
||||
packet = Packet(out_size)
|
||||
memcpy(packet.ptr.data, out_data, out_size)
|
||||
|
||||
packets.append(packet)
|
||||
|
||||
if not in_size:
|
||||
# This was a flush. Only one packet should ever be returned.
|
||||
break
|
||||
|
||||
in_data += consumed
|
||||
in_size -= consumed
|
||||
|
||||
if not in_size:
|
||||
break
|
||||
|
||||
return packets
|
||||
|
||||
@property
|
||||
def is_hwaccel(self):
|
||||
"""
|
||||
Returns ``True`` if this codec context is hardware accelerated, ``False`` otherwise.
|
||||
"""
|
||||
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)
|
||||
err_check(res, "avcodec_send_frame()")
|
||||
|
||||
packet = self._recv_packet()
|
||||
while packet:
|
||||
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):
|
||||
return [frame]
|
||||
|
||||
cdef Frame _alloc_next_frame(self):
|
||||
raise NotImplementedError("Base CodecContext cannot decode.")
|
||||
|
||||
cdef _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:
|
||||
res = lib.avcodec_receive_frame(self.ptr, frame.ptr)
|
||||
|
||||
if res == -EAGAIN or res == lib.AVERROR_EOF:
|
||||
return
|
||||
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):
|
||||
return frame
|
||||
|
||||
cdef _recv_packet(self):
|
||||
cdef Packet packet = Packet()
|
||||
|
||||
cdef int res
|
||||
with 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()")
|
||||
|
||||
if not res:
|
||||
return packet
|
||||
|
||||
cdef _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.")
|
||||
|
||||
self.open(strict=False)
|
||||
|
||||
frames = self._prepare_frames_for_encode(frame)
|
||||
|
||||
# Assert the frames are in our time base.
|
||||
# TODO: Don't mutate time.
|
||||
for frame in frames:
|
||||
if frame is not None:
|
||||
frame._rebase_time(self.ptr.time_base)
|
||||
|
||||
return frames
|
||||
|
||||
cpdef encode(self, Frame frame=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):
|
||||
for packet in self._send_frame_and_recv(frame):
|
||||
self._setup_encoded_packet(packet)
|
||||
res.append(packet)
|
||||
return res
|
||||
|
||||
def encode_lazy(self, Frame frame=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):
|
||||
# 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
|
||||
# a different time_base.
|
||||
#
|
||||
# NOTE: if the CodecContext's time_base is altered during encoding, all bets
|
||||
# are off!
|
||||
packet.ptr.time_base = self.ptr.time_base
|
||||
|
||||
cpdef decode(self, Packet packet=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).
|
||||
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
cpdef 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.
|
||||
when seeking or when switching to a different stream.
|
||||
|
||||
"""
|
||||
if self.is_open:
|
||||
with nogil:
|
||||
lib.avcodec_flush_buffers(self.ptr)
|
||||
|
||||
cdef _setup_decoded_frame(self, Frame frame, Packet packet):
|
||||
# Propagate our manual times.
|
||||
# While decoding, frame times are in stream time_base, which PyAV
|
||||
# is carrying around.
|
||||
# TODO: Somehow get this from the stream so we can not pass the
|
||||
# packet here (because flushing packets are bogus).
|
||||
if packet is not None:
|
||||
frame._time_base = packet.ptr.time_base
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.codec.name
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self.codec.type
|
||||
|
||||
@property
|
||||
def profiles(self):
|
||||
"""
|
||||
List the available profiles for this stream.
|
||||
|
||||
:type: list[str]
|
||||
"""
|
||||
ret = []
|
||||
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:
|
||||
ret.append(desc.profiles[i].name)
|
||||
i += 1
|
||||
|
||||
return ret
|
||||
|
||||
@property
|
||||
def profile(self):
|
||||
if not self.ptr.codec or not self.codec.desc or not self.codec.desc.profiles:
|
||||
return
|
||||
|
||||
# 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:
|
||||
if desc.profiles[i].profile == self.ptr.profile:
|
||||
return desc.profiles[i].name
|
||||
i += 1
|
||||
|
||||
@profile.setter
|
||||
def profile(self, value):
|
||||
if not self.codec or not self.codec.desc or not self.codec.desc.profiles:
|
||||
return
|
||||
|
||||
# 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:
|
||||
if desc.profiles[i].name == value:
|
||||
self.ptr.profile = desc.profiles[i].profile
|
||||
return
|
||||
i += 1
|
||||
|
||||
@property
|
||||
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)
|
||||
|
||||
@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)
|
||||
|
||||
@property
|
||||
def codec_tag(self):
|
||||
return self.ptr.codec_tag.to_bytes(4, byteorder="little", signed=False).decode(
|
||||
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)
|
||||
else:
|
||||
raise ValueError("Codec tag should be a 4 character string.")
|
||||
|
||||
@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):
|
||||
self.ptr.bit_rate = value
|
||||
|
||||
@property
|
||||
def max_bit_rate(self):
|
||||
if self.ptr.rc_max_rate > 0:
|
||||
return self.ptr.rc_max_rate
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def bit_rate_tolerance(self):
|
||||
self.ptr.bit_rate_tolerance
|
||||
|
||||
@bit_rate_tolerance.setter
|
||||
def bit_rate_tolerance(self, int value):
|
||||
self.ptr.bit_rate_tolerance = value
|
||||
|
||||
@property
|
||||
def thread_count(self):
|
||||
"""How many threads to use; 0 means auto.
|
||||
|
||||
Wraps :ffmpeg:`AVCodecContext.thread_count`.
|
||||
|
||||
"""
|
||||
return self.ptr.thread_count
|
||||
|
||||
@thread_count.setter
|
||||
def thread_count(self, int value):
|
||||
if self.is_open:
|
||||
raise RuntimeError("Cannot change thread_count after codec is open.")
|
||||
self.ptr.thread_count = value
|
||||
|
||||
@property
|
||||
def thread_type(self):
|
||||
"""One of :class:`.ThreadType`.
|
||||
|
||||
Wraps :ffmpeg:`AVCodecContext.thread_type`.
|
||||
|
||||
"""
|
||||
return ThreadType(self.ptr.thread_type)
|
||||
|
||||
@thread_type.setter
|
||||
def thread_type(self, value):
|
||||
if self.is_open:
|
||||
raise RuntimeError("Cannot change thread_type after codec is open.")
|
||||
if type(value) is int:
|
||||
self.ptr.thread_type = value
|
||||
elif type(value) is str:
|
||||
self.ptr.thread_type = ThreadType[value].value
|
||||
else:
|
||||
self.ptr.thread_type = value.value
|
||||
|
||||
@property
|
||||
def skip_frame(self):
|
||||
"""Returns one of the following str literals:
|
||||
|
||||
"NONE" Discard nothing
|
||||
"DEFAULT" Discard useless packets like 0 size packets in AVI
|
||||
"NONREF" Discard all non reference
|
||||
"BIDIR" Discard all bidirectional frames
|
||||
"NONINTRA" Discard all non intra frames
|
||||
"NONKEY Discard all frames except keyframes
|
||||
"ALL" Discard all
|
||||
|
||||
Wraps :ffmpeg:`AVCodecContext.skip_frame`.
|
||||
"""
|
||||
value = self.ptr.skip_frame
|
||||
if value == lib.AVDISCARD_NONE:
|
||||
return "NONE"
|
||||
if value == lib.AVDISCARD_DEFAULT:
|
||||
return "DEFAULT"
|
||||
if value == lib.AVDISCARD_NONREF:
|
||||
return "NONREF"
|
||||
if value == lib.AVDISCARD_BIDIR:
|
||||
return "BIDIR"
|
||||
if value == lib.AVDISCARD_NONINTRA:
|
||||
return "NONINTRA"
|
||||
if value == lib.AVDISCARD_NONKEY:
|
||||
return "NONKEY"
|
||||
if value == lib.AVDISCARD_ALL:
|
||||
return "ALL"
|
||||
return f"{value}"
|
||||
|
||||
@skip_frame.setter
|
||||
def skip_frame(self, value):
|
||||
if value == "NONE":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_NONE
|
||||
elif value == "DEFAULT":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_DEFAULT
|
||||
elif value == "NONREF":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_NONREF
|
||||
elif value == "BIDIR":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_BIDIR
|
||||
elif value == "NONINTRA":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_NONINTRA
|
||||
elif value == "NONKEY":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_NONKEY
|
||||
elif value == "ALL":
|
||||
self.ptr.skip_frame = lib.AVDISCARD_ALL
|
||||
else:
|
||||
raise ValueError("Invalid skip_frame type")
|
||||
|
||||
@property
|
||||
def delay(self):
|
||||
"""Codec delay.
|
||||
|
||||
Wraps :ffmpeg:`AVCodecContext.delay`.
|
||||
|
||||
"""
|
||||
return self.ptr.delay
|
||||
Binary file not shown.
21
venv/lib/python3.12/site-packages/av/codec/hwaccel.pxd
Normal file
21
venv/lib/python3.12/site-packages/av/codec/hwaccel.pxd
Normal file
@@ -0,0 +1,21 @@
|
||||
cimport libav as lib
|
||||
|
||||
from av.codec.codec cimport Codec
|
||||
|
||||
|
||||
cdef class HWConfig:
|
||||
cdef object __weakref__
|
||||
cdef lib.AVCodecHWConfig *ptr
|
||||
cdef void _init(self, lib.AVCodecHWConfig *ptr)
|
||||
|
||||
cdef HWConfig wrap_hwconfig(lib.AVCodecHWConfig *ptr)
|
||||
|
||||
cdef class HWAccel:
|
||||
cdef int _device_type
|
||||
cdef str _device
|
||||
cdef readonly Codec codec
|
||||
cdef readonly HWConfig config
|
||||
cdef lib.AVBufferRef *ptr
|
||||
cdef public bint allow_software_fallback
|
||||
cdef public dict options
|
||||
cdef public int flags
|
||||
50
venv/lib/python3.12/site-packages/av/codec/hwaccel.pyi
Normal file
50
venv/lib/python3.12/site-packages/av/codec/hwaccel.pyi
Normal file
@@ -0,0 +1,50 @@
|
||||
from enum import IntEnum
|
||||
from typing import cast
|
||||
|
||||
from av.codec.codec import Codec
|
||||
from av.video.format import VideoFormat
|
||||
|
||||
class HWDeviceType(IntEnum):
|
||||
none = cast(int, ...)
|
||||
vdpau = cast(int, ...)
|
||||
cuda = cast(int, ...)
|
||||
vaapi = cast(int, ...)
|
||||
dxva2 = cast(int, ...)
|
||||
qsv = cast(int, ...)
|
||||
videotoolbox = cast(int, ...)
|
||||
d3d11va = cast(int, ...)
|
||||
drm = cast(int, ...)
|
||||
opencl = cast(int, ...)
|
||||
mediacodec = cast(int, ...)
|
||||
vulkan = cast(int, ...)
|
||||
d3d12va = cast(int, ...)
|
||||
|
||||
class HWConfigMethod(IntEnum):
|
||||
none = cast(int, ...)
|
||||
hw_device_ctx = cast(int, ...)
|
||||
hw_frame_ctx = cast(int, ...)
|
||||
internal = cast(int, ...)
|
||||
ad_hoc = cast(int, ...)
|
||||
|
||||
class HWConfig:
|
||||
@property
|
||||
def device_type(self) -> HWDeviceType: ...
|
||||
@property
|
||||
def format(self) -> VideoFormat: ...
|
||||
@property
|
||||
def methods(self) -> HWConfigMethod: ...
|
||||
@property
|
||||
def is_supported(self) -> bool: ...
|
||||
|
||||
class HWAccel:
|
||||
def __init__(
|
||||
self,
|
||||
device_type: str | HWDeviceType,
|
||||
device: str | None = None,
|
||||
allow_software_fallback: bool = False,
|
||||
options: dict[str, object] | None = None,
|
||||
flags: int | None = None,
|
||||
) -> None: ...
|
||||
def create(self, codec: Codec) -> HWAccel: ...
|
||||
|
||||
def hwdevices_available() -> list[str]: ...
|
||||
159
venv/lib/python3.12/site-packages/av/codec/hwaccel.pyx
Normal file
159
venv/lib/python3.12/site-packages/av/codec/hwaccel.pyx
Normal file
@@ -0,0 +1,159 @@
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
amf = 13 # FFmpeg >=8
|
||||
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_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()
|
||||
|
||||
cdef HWConfig wrap_hwconfig(lib.AVCodecHWConfig *ptr):
|
||||
try:
|
||||
return _singletons[<int>ptr]
|
||||
except KeyError:
|
||||
pass
|
||||
cdef HWConfig config = HWConfig(_cinit_sentinel)
|
||||
config._init(ptr)
|
||||
_singletons[<int>ptr] = config
|
||||
return config
|
||||
|
||||
|
||||
cdef class HWConfig:
|
||||
def __init__(self, sentinel):
|
||||
if sentinel is not _cinit_sentinel:
|
||||
raise RuntimeError("Cannot instantiate CodecContext")
|
||||
|
||||
cdef void _init(self, lib.AVCodecHWConfig *ptr):
|
||||
self.ptr = ptr
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
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}>"
|
||||
)
|
||||
|
||||
@property
|
||||
def device_type(self):
|
||||
return HWDeviceType(self.ptr.device_type)
|
||||
|
||||
@property
|
||||
def format(self):
|
||||
return get_video_format(self.ptr.pix_fmt, 0, 0)
|
||||
|
||||
@property
|
||||
def methods(self):
|
||||
return HWConfigMethod(self.ptr.methods)
|
||||
|
||||
@property
|
||||
def is_supported(self):
|
||||
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
|
||||
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):
|
||||
if isinstance(device_type, HWDeviceType):
|
||||
self._device_type = device_type
|
||||
elif isinstance(device_type, str):
|
||||
self._device_type = int(lib.av_hwdevice_find_type_by_name(device_type))
|
||||
elif isinstance(device_type, int):
|
||||
self._device_type = device_type
|
||||
else:
|
||||
raise ValueError("Unknown type for device_type")
|
||||
|
||||
self._device = device
|
||||
self.allow_software_fallback = allow_software_fallback
|
||||
self.options = {} if not options else dict(options)
|
||||
self.flags = 0 if not flags else flags
|
||||
self.ptr = NULL
|
||||
self.config = None
|
||||
|
||||
def _initialize_hw_context(self, Codec codec not None):
|
||||
cdef HWConfig config
|
||||
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:
|
||||
raise NotImplementedError(f"No supported hardware config for {codec}")
|
||||
|
||||
self.config = config
|
||||
|
||||
cdef char *c_device = NULL
|
||||
if self._device:
|
||||
device_bytes = self._device.encode()
|
||||
c_device = device_bytes
|
||||
cdef _Dictionary c_options = Dictionary(self.options)
|
||||
|
||||
err_check(
|
||||
lib.av_hwdevice_ctx_create(
|
||||
&self.ptr, config.ptr.device_type, c_device, c_options.ptr, self.flags
|
||||
)
|
||||
)
|
||||
|
||||
def create(self, Codec codec not None):
|
||||
"""Create a new hardware accelerator context with the given codec"""
|
||||
if self.ptr:
|
||||
raise RuntimeError("Hardware context already initialized")
|
||||
|
||||
ret = HWAccel(
|
||||
device_type=self._device_type,
|
||||
device=self._device,
|
||||
allow_software_fallback=self.allow_software_fallback,
|
||||
options=self.options
|
||||
)
|
||||
ret._initialize_hw_context(codec)
|
||||
return ret
|
||||
|
||||
def __dealloc__(self):
|
||||
if self.ptr:
|
||||
lib.av_buffer_unref(&self.ptr)
|
||||
Reference in New Issue
Block a user