Voice et bot modif

This commit is contained in:
pi 2026-06-16 17:09:34 +00:00
parent 189d56026b
commit 7333a22bcd
10774 changed files with 634644 additions and 933308 deletions

View file

@ -1,35 +1,38 @@
import os
from fractions import Fraction
import cython
from cython.cimports import libav as lib
from cython.cimports.av.codec.codec import Codec
from cython.cimports.av.codec.context import CodecContext, wrap_codec_context
from cython.cimports.av.container.streams import StreamContainer
from cython.cimports.av.dictionary import _Dictionary
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.stream import Stream, wrap_stream
from cython.cimports.av.utils import dict_to_avdict, to_avrational
from av.dictionary import Dictionary
@cython.cfunc
def close_output(self: OutputContainer):
self.streams = StreamContainer()
if self._started and not self._done:
if self._myflag & 12 == 4: # enum.started and not enum.done
# If the underlying Python IO file was already closed (e.g. during GC
# finalization where cycle ordering is undefined), skip the trailer.
if self.file is not None and getattr(self.file.file, "closed", False):
self._myflag |= 8 # enum.done = True
return
# We must only ever call av_write_trailer *once*, otherwise we get a
# segmentation fault. Therefore no matter whether it succeeds or not
# we must absolutely set self._done.
# we must absolutely set enum.done.
try:
self.err_check(lib.av_write_trailer(self.ptr))
finally:
if self.file is None and not (self.ptr.oformat.flags & lib.AVFMT_NOFILE):
lib.avio_closep(cython.address(self.ptr.pb))
self._done = True
self._myflag |= 8 # enum.done = True
@cython.final
@cython.cclass
class OutputContainer(Container):
def __cinit__(self, *args, **kwargs):
@ -38,8 +41,10 @@ class OutputContainer(Container):
with cython.nogil:
self.packet_ptr = lib.av_packet_alloc()
def __dealloc__(self):
def __del__(self):
close_output(self)
def __dealloc__(self):
with cython.nogil:
lib.av_packet_free(cython.address(self.packet_ptr))
@ -90,7 +95,17 @@ class OutputContainer(Container):
# Some sane audio defaults
elif codec.type == lib.AVMEDIA_TYPE_AUDIO:
ctx.sample_fmt = codec.sample_fmts[0]
out: cython.pointer[cython.const[cython.void]] = cython.NULL
lib.avcodec_get_supported_config(
cython.NULL,
codec,
lib.AV_CODEC_CONFIG_SAMPLE_FORMAT,
0,
cython.address(out),
cython.NULL,
)
if out:
ctx.sample_fmt = cython.cast(cython.pointer[lib.AVSampleFormat], out)[0]
ctx.bit_rate = kwargs.pop("bit_rate", 0)
ctx.bit_rate_tolerance = kwargs.pop("bit_rate_tolerance", 32000)
try:
@ -130,6 +145,83 @@ class OutputContainer(Container):
return py_stream
def add_mux_stream(self, codec_name: str, rate=None, **kwargs) -> Stream:
"""add_mux_stream(codec_name, rate=None)
Creates a new stream for muxing pre-encoded data without creating a
:class:`.CodecContext`. Use this when you want to mux packets that were
already encoded externally and no encoding/decoding is needed.
:param codec_name: The name of a codec.
:type codec_name: str
:param \\**kwargs: Set attributes for the stream (e.g. ``width``, ``height``,
``time_base``).
:rtype: The new :class:`~av.stream.Stream`.
"""
# Find the codec to get its id and type (try encoder first, then decoder).
codec_name_bytes: bytes = codec_name.encode()
codec: cython.pointer[cython.const[lib.AVCodec]] = (
lib.avcodec_find_encoder_by_name(codec_name_bytes)
)
codec_descriptor: cython.pointer[cython.const[lib.AVCodecDescriptor]] = (
cython.NULL
)
if codec == cython.NULL:
codec = lib.avcodec_find_decoder_by_name(codec_name_bytes)
if codec == cython.NULL:
codec_descriptor = lib.avcodec_descriptor_get_by_name(codec_name_bytes)
if codec_descriptor == cython.NULL:
raise ValueError(f"Unknown codec: {codec_name!r}")
codec_id: lib.AVCodecID
codec_type: lib.AVMediaType
if codec != cython.NULL:
codec_id = codec.id
codec_type = codec.type
else:
codec_id = codec_descriptor.id
codec_type = codec_descriptor.type
# Assert that this format supports the requested codec.
if not lib.avformat_query_codec(
self.ptr.oformat, codec_id, lib.FF_COMPLIANCE_NORMAL
):
raise ValueError(
f"{self.format.name!r} format does not support {codec_name!r} codec"
)
# Create stream with no codec context.
stream: cython.pointer[lib.AVStream] = lib.avformat_new_stream(
self.ptr, cython.NULL
)
if stream == cython.NULL:
raise MemoryError("Could not allocate stream")
stream.codecpar.codec_id = codec_id
stream.codecpar.codec_type = codec_type
if codec_type == lib.AVMEDIA_TYPE_VIDEO:
stream.codecpar.width = kwargs.pop("width", 0)
stream.codecpar.height = kwargs.pop("height", 0)
if rate is not None:
to_avrational(rate, cython.address(stream.avg_frame_rate))
elif codec_type == lib.AVMEDIA_TYPE_AUDIO:
if rate is not None:
if type(rate) is int:
stream.codecpar.sample_rate = rate
else:
raise TypeError("audio stream `rate` must be: int | None")
# Construct the user-land stream (no codec context).
py_stream: Stream = wrap_stream(self, stream, None)
self.streams.add_stream(py_stream)
for k, v in kwargs.items():
setattr(py_stream, k, v)
return py_stream
def add_stream_from_template(
self, template: Stream, opaque: bool | None = None, **kwargs
):
@ -171,6 +263,10 @@ class OutputContainer(Container):
# Reset the codec tag assuming we are remuxing.
ctx.codec_tag = 0
# Copy the template's stream time_base
stream.time_base = template.ptr.time_base
ctx.time_base = template.ptr.time_base
# Some formats want stream headers to be separate
if self.ptr.oformat.flags & lib.AVFMT_GLOBALHEADER:
ctx.flags |= lib.AV_CODEC_FLAG_GLOBAL_HEADER
@ -186,6 +282,7 @@ class OutputContainer(Container):
# Construct the user-land stream
py_codec_context: CodecContext = wrap_codec_context(ctx, codec, None)
py_codec_context._ctxflags |= 1 # _template_initialized = True
py_stream: Stream = wrap_stream(self, stream, py_codec_context)
self.streams.add_stream(py_stream)
@ -278,16 +375,17 @@ class OutputContainer(Container):
:rtype: The new :class:`~av.data.stream.DataStream`.
"""
codec: cython.pointer[cython.const[lib.AVCodec]] = cython.NULL
codec_descriptor: cython.pointer[lib.AVCodecDescriptor] = cython.NULL
codec_descriptor: cython.pointer[cython.const[lib.AVCodecDescriptor]] = (
cython.NULL
)
if codec_name is not None:
codec = lib.avcodec_find_encoder_by_name(codec_name.encode())
codec_name_bytes: bytes = codec_name.encode()
codec = lib.avcodec_find_encoder_by_name(codec_name_bytes)
if codec == cython.NULL:
codec = lib.avcodec_find_decoder_by_name(codec_name.encode())
codec = lib.avcodec_find_decoder_by_name(codec_name_bytes)
if codec == cython.NULL:
codec_descriptor = lib.avcodec_descriptor_get_by_name(
codec_name.encode()
)
codec_descriptor = lib.avcodec_descriptor_get_by_name(codec_name_bytes)
if codec_descriptor == cython.NULL:
raise ValueError(f"Unknown data codec: {codec_name}")
@ -340,7 +438,7 @@ class OutputContainer(Container):
@cython.ccall
def start_encoding(self):
"""Write the file header! Called automatically."""
if self._started:
if self._myflag & 4: # started
return
# TODO: This does NOT handle options coming from 3 sources.
@ -351,14 +449,11 @@ class OutputContainer(Container):
# Finalize and open all streams.
for stream in self.streams:
ctx = stream.codec_context
# Skip codec context handling for streams without codecs (e.g. data/attachments).
if ctx is None:
if stream.type not in {"data", "attachment"}:
raise ValueError(f"Stream {stream.index} has no codec context")
else:
if not ctx.is_open:
for k, v in self.options.items():
ctx.options.setdefault(k, v)
if ctx is not None and not ctx.is_open:
for k, v in self.options.items():
ctx.options.setdefault(k, v)
if not (ctx._ctxflags & 1): # template_initialized
ctx.open()
# Track option consumption.
@ -384,8 +479,8 @@ class OutputContainer(Container):
errors=self.metadata_errors,
)
all_options: _Dictionary = Dictionary(self.options, self.container_options)
options: _Dictionary = all_options.copy()
all_options: Dictionary = Dictionary(self.options, self.container_options)
options: Dictionary = all_options.copy()
self.err_check(lib.avformat_write_header(self.ptr, cython.address(options.ptr)))
# Track option usage...
@ -403,7 +498,7 @@ class OutputContainer(Container):
log = logging.getLogger(__name__)
log.warning("Some options were not used: %s" % unused_options)
self._started = True
self._myflag |= 4
@property
def supported_codecs(self):