From cf1bf7b872aafeb66b54260a916ca523b27d6e3c Mon Sep 17 00:00:00 2001 From: WyattBlue Date: Mon, 2 Nov 2026 14:18:16 -0500 Subject: [PATCH] Share AudioFrame plane count with AudioPlane Factor the plane-count formula into AudioFrame._nb_planes() so AudioFrame.planes and AudioPlane.__cinit__ cannot disagree, and add a regression test for layouts with more than 8 channels. --- av/audio/frame.pxd | 1 + av/audio/frame.py | 6 +++++- av/audio/plane.py | 4 +--- tests/test_audioframe.py | 11 +++++++++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/av/audio/frame.pxd b/av/audio/frame.pxd index c1daa283d..7db0714a6 100644 --- a/av/audio/frame.pxd +++ b/av/audio/frame.pxd @@ -27,5 +27,6 @@ cdef class AudioFrame(Frame): cdef void _init(self, lib.AVSampleFormat format, lib.AVChannelLayout layout, unsigned int nb_samples, unsigned int align) cdef void _init_user_attributes(self) + cdef int _nb_planes(self) cdef AudioFrame alloc_audio_frame() diff --git a/av/audio/frame.py b/av/audio/frame.py index db4575e45..9cc644bb8 100644 --- a/av/audio/frame.py +++ b/av/audio/frame.py @@ -141,6 +141,10 @@ def from_ndarray(array, format="s16", layout="stereo"): plane.update(array[i, :]) return frame + @cython.cfunc + def _nb_planes(self) -> cython.int: + return self.layout.nb_channels if self.format.is_planar else 1 + @property def planes(self): """ @@ -148,7 +152,7 @@ def planes(self): :type: tuple """ - nb_planes: cython.int = self.layout.nb_channels if self.format.is_planar else 1 + nb_planes: cython.int = self._nb_planes() plane_count: cython.int = 0 while plane_count < nb_planes and self.ptr.extended_data[plane_count]: plane_count += 1 diff --git a/av/audio/plane.py b/av/audio/plane.py index ab37ff217..cb1443c64 100644 --- a/av/audio/plane.py +++ b/av/audio/plane.py @@ -6,9 +6,7 @@ @cython.cclass class AudioPlane(Plane): def __cinit__(self, frame: AudioFrame, index: cython.int): - nb_planes: cython.int = ( - frame.layout.nb_channels if frame.format.is_planar else 1 - ) + nb_planes: cython.int = frame._nb_planes() if index < 0 or index >= nb_planes: raise ValueError(f"plane index {index} out of range for {nb_planes} planes") diff --git a/tests/test_audioframe.py b/tests/test_audioframe.py index 80c06de0e..37c3bdef0 100644 --- a/tests/test_audioframe.py +++ b/tests/test_audioframe.py @@ -56,6 +56,17 @@ def test_manual_fltp_7_1_constructor() -> None: assertNdarraysEqual(frame.to_ndarray(), array) +def test_manual_fltp_22_2_constructor() -> None: + # More than 8 planes: extended_data is a separate heap array of exactly + # nb_channels pointers, so reading past it would be out of bounds. + frame = AudioFrame(format="fltp", layout="22.2", samples=160) + assert len(frame.planes) == 24 + + array = np.arange(24 * 160, dtype="f4").reshape(24, 160) + frame = AudioFrame.from_ndarray(array, format="fltp", layout="22.2") + assertNdarraysEqual(frame.to_ndarray(), array) + + def test_manual_s16_mono_constructor() -> None: frame = AudioFrame(format="s16", layout="mono", samples=160) assert frame.format.name == "s16"