diff --git a/av/container/output.py b/av/container/output.py index 44032bacd..1f424e10a 100644 --- a/av/container/output.py +++ b/av/container/output.py @@ -282,6 +282,8 @@ def add_stream_from_template( :param \\**kwargs: Set attributes for the stream. :rtype: The new :class:`~av.stream.Stream`. """ + template.container._assert_open() + if opaque is None: opaque = template.type != "video" diff --git a/av/filter/context.py b/av/filter/context.py index 1dfd6e7a4..1ec3674d1 100644 --- a/av/filter/context.py +++ b/av/filter/context.py @@ -1,5 +1,3 @@ -import weakref - import cython import cython.cimports.libav as lib from cython.cimports.av.audio.frame import alloc_audio_frame @@ -23,7 +21,7 @@ def wrap_filter_context( graph: Graph, filter: Filter, ptr: cython.pointer[lib.AVFilterContext] ) -> FilterContext: self: FilterContext = FilterContext(_cinit_sentinel) - self._graph = weakref.ref(graph) + self._graph = graph self.filter = filter self.ptr = ptr @@ -110,14 +108,16 @@ def link_to( @property def graph(self): - if graph := self._graph(): - return graph - else: - raise RuntimeError("graph is unallocated") + return self._graph def push(self, frame: Frame | None): res: cython.int + # av_buffersrc_write_frame() dereferences graph internals that only + # exist after configuration; pushing first would segfault. + if self._kind == _KIND_SOURCE or frame is None: + self._graph.configure() + if frame is None: with cython.nogil: res = lib.av_buffersrc_write_frame(self.ptr, cython.NULL) diff --git a/av/filter/graph.pxd b/av/filter/graph.pxd index bf22cd34d..bedf924e9 100644 --- a/av/filter/graph.pxd +++ b/av/filter/graph.pxd @@ -4,21 +4,18 @@ from av.filter.context cimport FilterContext cdef class Graph: + # Fields are laid out in declaration order: pointers first, then the two + # ints paired up, so there are no padding holes between them. cdef object __weakref__ - cdef lib.AVFilterGraph *ptr - + cdef dict _name_counts + cdef dict[size_t, FilterContext] _context_by_ptr + cdef dict[str, list[FilterContext]] _context_by_type cdef readonly bint configured - cpdef configure(self, bint auto_buffer=*, bint force=*) + cdef int _nb_filters_seen - cdef dict _name_counts + cpdef configure(self, bint auto_buffer=*, bint force=*) cdef str _get_unique_name(self, str name) cdef list[FilterContext] _get_context_by_type(self, str type) - cdef void _register_context(self, FilterContext) cdef void _auto_register(self) - cdef int _nb_filters_seen - cdef dict[size_t, FilterContext] _context_by_ptr - cdef dict[str, list[FilterContext]] _context_by_type - cdef list[FilterContext] _video_sources - cdef list[FilterContext] _audio_sources diff --git a/av/filter/graph.py b/av/filter/graph.py index d2409a0dc..d2067facb 100644 --- a/av/filter/graph.py +++ b/av/filter/graph.py @@ -22,8 +22,6 @@ def __cinit__(self): self._nb_filters_seen = 0 self._context_by_ptr = {} self._context_by_type = {} - self._video_sources = [] - self._audio_sources = [] def __dealloc__(self): if self.ptr: @@ -113,10 +111,6 @@ def _register_context(self, ctx: FilterContext) -> cython.void: name: str = ctx.filter.ptr.name self._context_by_ptr[cython.cast(cython.size_t, ctx.ptr)] = ctx self._context_by_type.setdefault(name, []).append(ctx) - if name == "buffer": - self._video_sources.append(ctx) - elif name == "abuffer": - self._audio_sources.append(ctx) @cython.cfunc def _auto_register(self) -> cython.void: @@ -250,11 +244,13 @@ def push(self, frame, at: cython.int = -1): every buffer source matching the frame's type. """ if frame is None: - contexts = self._video_sources + self._audio_sources + contexts = self._get_context_by_type("buffer") + self._get_context_by_type( + "abuffer" + ) elif isinstance(frame, VideoFrame): - contexts = self._video_sources + contexts = self._get_context_by_type("buffer") elif isinstance(frame, AudioFrame): - contexts = self._audio_sources + contexts = self._get_context_by_type("abuffer") else: raise ValueError( f"can only AudioFrame, VideoFrame or None; got {type(frame)}" @@ -273,7 +269,7 @@ def push(self, frame, at: cython.int = -1): def vpush(self, frame: VideoFrame | None, at: cython.int = -1): """Like :meth:`push`, but only for :class:`.VideoFrame`.""" - contexts = self._video_sources + contexts = self._get_context_by_type("buffer") if at >= 0: if at >= len(contexts): raise IndexError( diff --git a/av/filter/link.py b/av/filter/link.py index d9a9bddf5..6cc3b023a 100644 --- a/av/filter/link.py +++ b/av/filter/link.py @@ -89,6 +89,13 @@ def is_output(self): def name(self): return lib.avfilter_pad_get_name(self.base_ptr, self.index) + @property + def type(self): + media_type = lib.av_get_media_type_string( + lib.avfilter_pad_get_type(self.base_ptr, self.index) + ) + return "unknown" if media_type == cython.NULL else media_type + @cython.final @cython.cclass