From 7e38c1824e7bc2910704f7e2bb0a24e8f89e1ffa Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Wed, 22 Apr 2026 23:42:28 -0400 Subject: [PATCH 1/2] Fix #2010 Also raise error if format lazy adds streams. --- av/container/input.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/av/container/input.py b/av/container/input.py index c7e82a84b..81d167c0d 100644 --- a/av/container/input.py +++ b/av/container/input.py @@ -34,13 +34,21 @@ def __cinit__(self, *args, **kwargs): c_options: cython.pointer[cython.pointer[lib.AVDictionary]] = cython.NULL base_dict: Dictionary stream_dict: Dictionary - if self.options or self.stream_options: + nb_streams_before: cython.uint = self.ptr.nb_streams + if self.stream_options and nb_streams_before == 0: + raise ValueError( + "stream_options were provided, but this format does not expose " + "its streams before avformat_find_stream_info (e.g. MPEG). " + "Per-stream options cannot be applied." + ) + # Only allocate c_options when streams are already known. + if (self.options or self.stream_options) and nb_streams_before > 0: base_dict = Dictionary(self.options) c_options = cython.cast( cython.pointer[cython.pointer[lib.AVDictionary]], - malloc(self.ptr.nb_streams * cython.sizeof(cython.p_void)), + malloc(nb_streams_before * cython.sizeof(cython.p_void)), ) - for i in range(self.ptr.nb_streams): + for i in range(nb_streams_before): c_options[i] = cython.NULL if i < len(self.stream_options) and self.stream_options: stream_dict = base_dict.copy() @@ -56,9 +64,8 @@ def __cinit__(self, *args, **kwargs): self.set_timeout(None) self.err_check(ret) - # Clean up all of our options. if c_options: - for i in range(self.ptr.nb_streams): + for i in range(nb_streams_before): lib.av_dict_free(cython.address(c_options[i])) free(c_options) From 7127922a19f5a5e43f01ae6d5240cfb0a128e03b Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Thu, 23 Apr 2026 00:03:10 -0400 Subject: [PATCH 2/2] Fix more wrong uses of malloc --- av/container/core.py | 5 +++++ av/container/input.py | 2 ++ tests/test_chapters.py | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/av/container/core.py b/av/container/core.py index ab6c889b7..4195be248 100755 --- a/av/container/core.py +++ b/av/container/core.py @@ -435,6 +435,11 @@ def set_chapters(self, chapters): with cython.nogil: _free_chapters(self.ptr) + if count == 0: + self.ptr.nb_chapters = 0 + self.ptr.chapters = cython.NULL + return + ch_array = cython.cast( AVChapterPtrPtr, lib.av_malloc(count * cython.sizeof(cython.pointer[lib.AVChapter])), diff --git a/av/container/input.py b/av/container/input.py index 81d167c0d..63792e02a 100644 --- a/av/container/input.py +++ b/av/container/input.py @@ -151,6 +151,8 @@ def demux(self, *args, **kwargs): self._assert_open() streams: list[Stream] = self.streams.get(*args, **kwargs) + if self.ptr.nb_streams == 0: + return include_stream: cython.pointer[cython.bint] = cython.cast( cython.pointer[cython.bint], malloc(self.ptr.nb_streams * cython.sizeof(bint)), diff --git a/tests/test_chapters.py b/tests/test_chapters.py index c8a3626ce..21272f527 100644 --- a/tests/test_chapters.py +++ b/tests/test_chapters.py @@ -56,3 +56,11 @@ def test_set_chapters() -> None: with av.open(path) as container: container.set_chapters(chapters) assert container.chapters() == chapters + + +def test_set_chapters_empty() -> None: + path = fate_suite("vorbis/vorbis_chapter_extension_demo.ogg") + with av.open(path) as container: + assert len(container.chapters()) == 4 + container.set_chapters([]) + assert container.chapters() == []