From 70e25312e5df6bc99c9e9df7e6dea3a684a182df Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Wed, 28 Oct 2026 13:12:06 -0400 Subject: [PATCH] Move program code to container/input --- av/container/input.pxd | 8 +++ av/container/input.py | 118 +++++++++++++++++++++++++++++++++++++- av/container/input.pyi | 24 +++++++- av/container/program.pxd | 11 ---- av/container/program.py | 121 --------------------------------------- av/container/program.pyi | 24 -------- docs/api/container.rst | 6 +- tests/test_programs.py | 2 +- 8 files changed, 149 insertions(+), 165 deletions(-) delete mode 100644 av/container/program.pxd delete mode 100644 av/container/program.py delete mode 100644 av/container/program.pyi diff --git a/av/container/input.pxd b/av/container/input.pxd index 42e07eb82..58c29d7ed 100644 --- a/av/container/input.pxd +++ b/av/container/input.pxd @@ -6,3 +6,11 @@ from av.stream cimport Stream cdef class InputContainer(Container): cdef void flush_buffers(self) + + +cdef class Program: + cdef InputContainer container + cdef readonly int index + cdef lib.AVProgram *_ptr(self) + +cdef Program wrap_program(InputContainer container, int index) diff --git a/av/container/input.py b/av/container/input.py index 1219e6d36..4ca39860a 100644 --- a/av/container/input.py +++ b/av/container/input.py @@ -1,6 +1,5 @@ import cython from cython.cimports.av.codec.context import CodecContext, wrap_codec_context -from cython.cimports.av.container.program import wrap_program from cython.cimports.av.container.streams import StreamContainer from cython.cimports.av.dictionary import Dictionary from cython.cimports.av.packet import Packet @@ -9,6 +8,10 @@ from cython.cimports.libc.stdint import int64_t, uint8_t from cython.cimports.libc.stdlib import free, malloc +from av.stream import Discard + +_cinit_bypass_sentinel = cython.declare(object, object()) + @cython.cfunc def close_input(self: InputContainer) -> cython.void: @@ -388,3 +391,116 @@ def flush_buffers(self) -> cython.void: codec_context = stream.cctx if codec_context: codec_context.flush_buffers() + + +@cython.cfunc +def wrap_program(container: InputContainer, index: cython.int) -> Program: + obj: Program = Program(_cinit_bypass_sentinel) + obj.container = container + obj.index = index + return obj + + +@cython.final +@cython.cclass +class Program: + """A program in a container: a group of streams that play together. + + MPEG-TS carries several programs (TV channels, in broadcast) in one stream. + Other formats, such as HLS, use programs for their variants. + + Wraps :ffmpeg:`AVProgram`. Get one from :attr:`.InputContainer.programs`. + """ + + def __cinit__(self, sentinel): + if sentinel is not _cinit_bypass_sentinel: + raise RuntimeError("cannot manually instantiate Program") + + @cython.cfunc + def _ptr(self) -> cython.pointer[lib.AVProgram]: + self.container._assert_open() + return self.container.ptr.programs[self.index] + + def __repr__(self): + if self.container.ptr == cython.NULL: + return f"" + return ( + f"" + ) + + @property + def id(self): + """The format-specific ID of this program. In MPEG-TS, this is the + program number (service ID). + + :type: int + """ + return self._ptr().id + + @property + def program_num(self): + """The MPEG-TS program number, or 0 for other formats. + + :type: int + """ + return self._ptr().program_num + + @property + def pmt_pid(self): + """The PID of this program's Program Map Table in MPEG-TS. + + :type: int + """ + return self._ptr().pmt_pid + + @property + def pcr_pid(self): + """The PID carrying this program's clock reference in MPEG-TS. + + :type: int + """ + return self._ptr().pcr_pid + + @property + def metadata(self): + """Metadata for this program, such as ``service_name`` and + ``service_provider`` in MPEG-TS. + + :type: dict[str, str] + """ + return avdict_to_dict(self._ptr().metadata) + + @property + def streams(self): + """The streams that belong to this program. + + A stream can belong to more than one program. + + :type: tuple[Stream, ...] + """ + ptr: cython.pointer[lib.AVProgram] = self._ptr() + by_index = {stream.index: stream for stream in self.container.streams} + result = [] + i: cython.uint + for i in range(ptr.nb_stream_indexes): + stream = by_index.get(ptr.stream_index[i]) + if stream is not None: + result.append(stream) + return tuple(result) + + @property + def discard(self): + """Controls whether the demuxer reads this program's packets. + + Set to :attr:`.Discard.all` to skip a program entirely. In MPEG-TS, a + stream's packets are only dropped once every program it belongs to is + discarded. + + :type: Discard + """ + return Discard(self._ptr().discard) + + @discard.setter + def discard(self, value): + self._ptr().discard = Discard(value).value diff --git a/av/container/input.pyi b/av/container/input.pyi index ae598bcee..b309e6a0f 100644 --- a/av/container/input.pyi +++ b/av/container/input.pyi @@ -4,16 +4,36 @@ from typing import Literal, final, overload from av.audio.frame import AudioFrame from av.audio.stream import AudioStream from av.packet import Packet -from av.stream import AttachmentStream, DataStream, Stream +from av.stream import AttachmentStream, DataStream, Discard, Stream from av.subtitles.stream import SubtitleStream from av.subtitles.subtitle import SubtitleSet from av.video.frame import VideoFrame from av.video.stream import VideoStream from .core import Container -from .program import Program from .streams import _Indices, _StreamSelector +@final +class Program: + @property + def index(self) -> int: ... + @property + def id(self) -> int: ... + @property + def program_num(self) -> int: ... + @property + def pmt_pid(self) -> int: ... + @property + def pcr_pid(self) -> int: ... + @property + def metadata(self) -> dict[str, str]: ... + @property + def streams(self) -> tuple[Stream, ...]: ... + @property + def discard(self) -> Discard: ... + @discard.setter + def discard(self, value: Discard | int) -> None: ... + @final class InputContainer(Container): start_time: int | None diff --git a/av/container/program.pxd b/av/container/program.pxd deleted file mode 100644 index c053266d1..000000000 --- a/av/container/program.pxd +++ /dev/null @@ -1,11 +0,0 @@ -cimport libav as lib - -from av.container.core cimport Container - - -cdef class Program: - cdef Container container - cdef readonly int index - cdef lib.AVProgram *_ptr(self) - -cdef Program wrap_program(Container container, int index) diff --git a/av/container/program.py b/av/container/program.py deleted file mode 100644 index dbfad5aab..000000000 --- a/av/container/program.py +++ /dev/null @@ -1,121 +0,0 @@ -import cython -import cython.cimports.libav as lib -from cython.cimports.av.container.core import Container -from cython.cimports.av.utils import avdict_to_dict - -from av.stream import Discard - -_cinit_bypass_sentinel = cython.declare(object, object()) - - -@cython.cfunc -def wrap_program(container: Container, index: cython.int) -> Program: - obj: Program = Program(_cinit_bypass_sentinel) - obj.container = container - obj.index = index - return obj - - -@cython.final -@cython.cclass -class Program: - """A program in a container: a group of streams that play together. - - MPEG-TS carries several programs (TV channels, in broadcast) in one stream. - Other formats, such as HLS, use programs for their variants. - - Wraps :ffmpeg:`AVProgram`. Get one from :attr:`.InputContainer.programs`. - """ - - def __cinit__(self, sentinel): - if sentinel is not _cinit_bypass_sentinel: - raise RuntimeError("cannot manually instantiate Program") - - @cython.cfunc - def _ptr(self) -> cython.pointer[lib.AVProgram]: - self.container._assert_open() - return self.container.ptr.programs[self.index] - - def __repr__(self): - if self.container.ptr == cython.NULL: - return f"" - return ( - f"" - ) - - @property - def id(self): - """The format-specific ID of this program. In MPEG-TS, this is the - program number (service ID). - - :type: int - """ - return self._ptr().id - - @property - def program_num(self): - """The MPEG-TS program number, or 0 for other formats. - - :type: int - """ - return self._ptr().program_num - - @property - def pmt_pid(self): - """The PID of this program's Program Map Table in MPEG-TS. - - :type: int - """ - return self._ptr().pmt_pid - - @property - def pcr_pid(self): - """The PID carrying this program's clock reference in MPEG-TS. - - :type: int - """ - return self._ptr().pcr_pid - - @property - def metadata(self): - """Metadata for this program, such as ``service_name`` and - ``service_provider`` in MPEG-TS. - - :type: dict[str, str] - """ - return avdict_to_dict(self._ptr().metadata) - - @property - def streams(self): - """The streams that belong to this program. - - A stream can belong to more than one program. - - :type: tuple[Stream, ...] - """ - ptr: cython.pointer[lib.AVProgram] = self._ptr() - by_index = {stream.index: stream for stream in self.container.streams} - result = [] - i: cython.uint - for i in range(ptr.nb_stream_indexes): - stream = by_index.get(ptr.stream_index[i]) - if stream is not None: - result.append(stream) - return tuple(result) - - @property - def discard(self): - """Controls whether the demuxer reads this program's packets. - - Set to :attr:`.Discard.all` to skip a program entirely. In MPEG-TS, a - stream's packets are only dropped once every program it belongs to is - discarded. - - :type: Discard - """ - return Discard(self._ptr().discard) - - @discard.setter - def discard(self, value): - self._ptr().discard = Discard(value).value diff --git a/av/container/program.pyi b/av/container/program.pyi deleted file mode 100644 index ab7f9d8fa..000000000 --- a/av/container/program.pyi +++ /dev/null @@ -1,24 +0,0 @@ -from typing import final - -from av.stream import Discard, Stream - -@final -class Program: - @property - def index(self) -> int: ... - @property - def id(self) -> int: ... - @property - def program_num(self) -> int: ... - @property - def pmt_pid(self) -> int: ... - @property - def pcr_pid(self) -> int: ... - @property - def metadata(self) -> dict[str, str]: ... - @property - def streams(self) -> tuple[Stream, ...]: ... - @property - def discard(self) -> Discard: ... - @discard.setter - def discard(self, value: Discard | int) -> None: ... diff --git a/docs/api/container.rst b/docs/api/container.rst index 1fb5537f2..18104ac86 100644 --- a/docs/api/container.rst +++ b/docs/api/container.rst @@ -41,13 +41,9 @@ Input Containers Programs -------- -.. currentmodule:: av.container.program - -.. autoclass:: Program +.. autoclass:: av.container.input.Program :members: -.. currentmodule:: av.container - Output Containers ----------------- diff --git a/tests/test_programs.py b/tests/test_programs.py index c4c1eecdb..7ffb3a7b9 100644 --- a/tests/test_programs.py +++ b/tests/test_programs.py @@ -3,7 +3,7 @@ import pytest import av -from av.container.program import Program +from av.container.input import Program from av.stream import Discard from .common import fate_suite