diff --git a/livekit-rtc/livekit/rtc/_utils.py b/livekit-rtc/livekit/rtc/_utils.py index b24451cd..bbaad0e1 100644 --- a/livekit-rtc/livekit/rtc/_utils.py +++ b/livekit-rtc/livekit/rtc/_utils.py @@ -40,28 +40,29 @@ def task_done_logger(task: asyncio.Task) -> None: return -def _buffer_supported_or_raise( +def _ensure_compatible_buffer( data: Union[bytes, bytearray, memoryview], -) -> None: - """Validate a buffer for FFI use. +) -> Union[bytes, bytearray, memoryview]: + """Validate and normalize a buffer for FFI use. - Raises clear errors for non-contiguous or sliced memoryviews. + Sliced memoryviews are materialized because get_address cannot + reliably resolve their offset for all buffer types. """ if isinstance(data, memoryview): if not data.contiguous: raise ValueError("memoryview must be contiguous") if data.nbytes != len(data.obj): # type: ignore[arg-type] - raise ValueError("sliced memoryviews are not supported") + return bytearray(data) if not data.readonly else bytes(data) elif not isinstance(data, (bytes, bytearray)): raise TypeError(f"expected bytes, bytearray, or memoryview, got {type(data)}") + return data -def get_address(data) -> int: +def get_address(data: Union[bytes, bytearray, memoryview]) -> int: if isinstance(data, memoryview): - _buffer_supported_or_raise(data) if not data.readonly: return ctypes.addressof(ctypes.c_char.from_buffer(data)) - data = data.obj + data = data.obj # type: ignore[assignment] if isinstance(data, bytearray): return ctypes.addressof(ctypes.c_char.from_buffer(data)) if isinstance(data, bytes): diff --git a/livekit-rtc/livekit/rtc/audio_frame.py b/livekit-rtc/livekit/rtc/audio_frame.py index 50f63899..1bd18745 100644 --- a/livekit-rtc/livekit/rtc/audio_frame.py +++ b/livekit-rtc/livekit/rtc/audio_frame.py @@ -15,7 +15,7 @@ import ctypes from ._ffi_client import FfiHandle from ._proto import audio_frame_pb2 as proto_audio -from ._utils import _buffer_supported_or_raise, get_address +from ._utils import _ensure_compatible_buffer, get_address from typing import Any, Union @@ -49,7 +49,7 @@ def __init__( Raises: ValueError: If the length of `data` is smaller than the required size. """ - _buffer_supported_or_raise(data) + data = _ensure_compatible_buffer(data) min_size = num_channels * samples_per_channel * ctypes.sizeof(ctypes.c_int16) data_len = len(data) diff --git a/livekit-rtc/livekit/rtc/video_frame.py b/livekit-rtc/livekit/rtc/video_frame.py index e6ff1ad1..ff0035e8 100644 --- a/livekit-rtc/livekit/rtc/video_frame.py +++ b/livekit-rtc/livekit/rtc/video_frame.py @@ -18,7 +18,7 @@ from ._proto import ffi_pb2 as proto from typing import List, Optional from ._ffi_client import FfiClient, FfiHandle -from ._utils import _buffer_supported_or_raise, get_address +from ._utils import _ensure_compatible_buffer, get_address from typing import Any @@ -48,7 +48,7 @@ def __init__( (e.g., RGBA, BGRA, RGB24, etc.). data (Union[bytes, bytearray, memoryview]): The raw pixel data for the video frame. """ - _buffer_supported_or_raise(data) + data = _ensure_compatible_buffer(data) self._width = width self._height = height