From 643edc8da63b15f52985536bebcf7f6bfe47bfcf Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Thu, 22 Jan 2026 22:19:28 -0500 Subject: [PATCH] Use zero-copy for Packet init from buffer data Instead of allocating new memory and copying data when creating a Packet from a buffer (bytes, numpy array, etc.), reference the source buffer directly using av_buffer_create with a noop_free callback. The ByteSource is stored in self.source to keep the Python buffer alive. Co-Authored-By: Claude Opus 4.5 --- av/opaque.pxd | 4 ++++ av/opaque.pyx | 4 ++++ av/packet.py | 27 ++++++++++++++++----------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/av/opaque.pxd b/av/opaque.pxd index 76174931f..808f6338e 100644 --- a/av/opaque.pxd +++ b/av/opaque.pxd @@ -1,4 +1,8 @@ cimport libav as lib +from libc.stdint cimport uint8_t + + +cdef void noop_free(void *opaque, uint8_t *data) noexcept nogil cdef class OpaqueContainer: diff --git a/av/opaque.pyx b/av/opaque.pyx index 626b02985..583ea5577 100644 --- a/av/opaque.pyx +++ b/av/opaque.pyx @@ -3,6 +3,10 @@ from libc.stdint cimport uint8_t, uintptr_t from libc.string cimport memcpy +cdef void noop_free(void *opaque, uint8_t *data) noexcept nogil: + pass + + cdef void key_free(void *opaque, uint8_t *data) noexcept nogil: cdef char *name = data with gil: diff --git a/av/packet.py b/av/packet.py index d21b432ea..867d46211 100644 --- a/av/packet.py +++ b/av/packet.py @@ -3,9 +3,9 @@ import cython from cython.cimports import libav as lib from cython.cimports.av.buffer import Buffer -from cython.cimports.av.bytesource import bytesource +from cython.cimports.av.bytesource import ByteSource, bytesource from cython.cimports.av.error import err_check -from cython.cimports.av.opaque import opaque_container +from cython.cimports.av.opaque import noop_free, opaque_container from cython.cimports.av.utils import avrational_to_fraction, to_avrational from cython.cimports.libc.string import memcpy @@ -213,24 +213,29 @@ def __dealloc__(self): def __init__(self, input=None): size: cython.size_t = 0 source: ByteSource = None + buf: cython.pointer[lib.AVBufferRef] if input is None: return if isinstance(input, int): size = input + if size: + err_check(lib.av_new_packet(self.ptr, size)) else: source = bytesource(input) size = source.length - - if size: - err_check(lib.av_new_packet(self.ptr, size)) - - if source is not None: - self.update(source) - # TODO: Hold onto the source, and copy its pointer - # instead of its data. - # self.source = source + if size: + # Create a buffer that references the source data directly. + # The noop_free callback is used because Python manages the memory + # via self.source keeping the ByteSource alive. + buf = lib.av_buffer_create(source.ptr, size, noop_free, cython.NULL, 0) + if buf == cython.NULL: + raise MemoryError("Could not allocate AVBufferRef") + self.ptr.buf = buf + self.ptr.data = source.ptr + self.ptr.size = size + self.source = source def __repr__(self): stream = self._stream.index if self._stream else 0