From 6e91c93adcb9f693cc04e8bec538af4082dc2e27 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Fri, 17 Apr 2026 02:18:43 +0100 Subject: [PATCH 1/2] Reuse a `AVPacket` read buffer when demuxing --- av/container/input.py | 18 ++++++++++++++---- include/avcodec.pxd | 2 ++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/av/container/input.py b/av/container/input.py index 97037457f..699497dca 100644 --- a/av/container/input.py +++ b/av/container/input.py @@ -153,6 +153,7 @@ def demux(self, *args, **kwargs): i: cython.uint packet: Packet + read_packet: Packet ret: cython.int self.set_timeout(self.read_timeout) @@ -165,22 +166,31 @@ def demux(self, *args, **kwargs): raise ValueError(f"stream index {i} out of range") include_stream[i] = True + # Pre-allocate a AVPacket that is reused as the read buffer. + read_packet = Packet() while True: - packet = Packet() + # Reset the read buffer + with cython.nogil: + lib.av_packet_unref(read_packet.ptr) try: self.start_timeout() with cython.nogil: - ret = lib.av_read_frame(self.ptr, packet.ptr) + ret = lib.av_read_frame(self.ptr, read_packet.ptr) self.err_check(ret) except EOFError: break - if include_stream[packet.ptr.stream_index]: + if include_stream[read_packet.ptr.stream_index]: # If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams # may also appear in av_read_frame(). # http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html # TODO: find better way to handle this - if packet.ptr.stream_index < len(self.streams): + if read_packet.ptr.stream_index < len(self.streams): + # Move the encoded data out of the read buffer into a + # fresh Packet for the caller. + packet = Packet() + with cython.nogil: + lib.av_packet_move_ref(packet.ptr, read_packet.ptr) packet._stream = self.streams[packet.ptr.stream_index] # Keep track of this so that remuxing is easier. packet.ptr.time_base = packet._stream.ptr.time_base diff --git a/include/avcodec.pxd b/include/avcodec.pxd index 8c610ea67..28dfcfed9 100644 --- a/include/avcodec.pxd +++ b/include/avcodec.pxd @@ -407,6 +407,8 @@ cdef extern from "libavcodec/avcodec.h" nogil: cdef void av_packet_free(AVPacket **) cdef int av_new_packet(AVPacket*, int) cdef int av_packet_ref(AVPacket *dst, const AVPacket *src) + cdef void av_packet_unref(AVPacket *pkt) + cdef void av_packet_move_ref(AVPacket *dst, AVPacket *src) cdef void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb) cdef enum AVSubtitleType: From b20db65631f32fa129a4d61aec9aed934577c6b2 Mon Sep 17 00:00:00 2001 From: Lukas Geiger Date: Fri, 17 Apr 2026 02:57:58 +0100 Subject: [PATCH 2/2] Use `AVPacket` directly Co-authored-by: WyattBlue --- av/container/input.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/av/container/input.py b/av/container/input.py index 699497dca..c7e82a84b 100644 --- a/av/container/input.py +++ b/av/container/input.py @@ -153,7 +153,7 @@ def demux(self, *args, **kwargs): i: cython.uint packet: Packet - read_packet: Packet + read_packet: cython.pointer[lib.AVPacket] ret: cython.int self.set_timeout(self.read_timeout) @@ -167,30 +167,34 @@ def demux(self, *args, **kwargs): include_stream[i] = True # Pre-allocate a AVPacket that is reused as the read buffer. - read_packet = Packet() + with cython.nogil: + read_packet = lib.av_packet_alloc() + if read_packet == cython.NULL: + raise MemoryError("Could not allocate packet") + while True: # Reset the read buffer with cython.nogil: - lib.av_packet_unref(read_packet.ptr) + lib.av_packet_unref(read_packet) try: self.start_timeout() with cython.nogil: - ret = lib.av_read_frame(self.ptr, read_packet.ptr) + ret = lib.av_read_frame(self.ptr, read_packet) self.err_check(ret) except EOFError: break - if include_stream[read_packet.ptr.stream_index]: + if include_stream[read_packet.stream_index]: # If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams # may also appear in av_read_frame(). # http://ffmpeg.org/doxygen/trunk/structAVFormatContext.html # TODO: find better way to handle this - if read_packet.ptr.stream_index < len(self.streams): + if read_packet.stream_index < len(self.streams): # Move the encoded data out of the read buffer into a # fresh Packet for the caller. packet = Packet() with cython.nogil: - lib.av_packet_move_ref(packet.ptr, read_packet.ptr) + lib.av_packet_move_ref(packet.ptr, read_packet) packet._stream = self.streams[packet.ptr.stream_index] # Keep track of this so that remuxing is easier. packet.ptr.time_base = packet._stream.ptr.time_base @@ -207,6 +211,8 @@ def demux(self, *args, **kwargs): finally: self.set_timeout(None) free(include_stream) + if read_packet != cython.NULL: + lib.av_packet_free(cython.address(read_packet)) def decode(self, *args, **kwargs): """decode(streams=None, video=None, audio=None, subtitles=None, data=None)