use cxx::UniquePtr; use livekit_utils::enum_dispatch; use std::pin::Pin; use std::slice; use vfb_sys::ffi::VideoFrameBufferType; use webrtc_sys::video_frame_buffer as vfb_sys; pub trait VideoFrameBufferTrait { fn width(&self) -> i32; fn height(&self) -> i32; fn to_i420(self) -> I420Buffer; } pub trait PlanarYuvBuffer: VideoFrameBufferTrait { fn chroma_width(&self) -> i32; fn chroma_height(&self) -> i32; fn stride_y(&self) -> i32; fn stride_u(&self) -> i32; fn stride_v(&self) -> i32; } pub trait PlanarYuv8Buffer: PlanarYuvBuffer { fn data_y(&self) -> &[u8]; fn data_u(&self) -> &[u8]; fn data_v(&self) -> &[u8]; } pub enum VideoFrameBuffer { Native(NativeBuffer), I420(I420Buffer), I420A(I420ABuffer), I422(I422Buffer), I444(I444Buffer), I010(I010Buffer), NV12(NV12Buffer), } impl VideoFrameBuffer { pub(crate) fn new(mut cxx_handle: UniquePtr) -> Self { unsafe { match cxx_handle.buffer_type() { VideoFrameBufferType::Native => Self::Native(NativeBuffer::new(cxx_handle)), VideoFrameBufferType::I420 => { Self::I420(I420Buffer::new(cxx_handle.pin_mut().get_i420())) } VideoFrameBufferType::I420A => Self::I420A(I420ABuffer::new(cxx_handle)), VideoFrameBufferType::I422 => Self::I422(I422Buffer::new(cxx_handle)), VideoFrameBufferType::I444 => Self::I444(I444Buffer::new(cxx_handle)), VideoFrameBufferType::I010 => Self::I010(I010Buffer::new(cxx_handle)), VideoFrameBufferType::NV12 => Self::NV12(NV12Buffer::new(cxx_handle)), _ => unreachable!(), // VideoFrameBufferType is represented as i32 } } } } impl VideoFrameBufferTrait for VideoFrameBuffer { enum_dispatch!( [Native, I420, I420A, I422, I444, I010, NV12] fnc!(width, &Self, [], i32); fnc!(height, &Self, [], i32); fnc!(to_i420, Self, [], I420Buffer); ); } macro_rules! recursive_cast { ($ptr:expr $(, $fnc:ident)*) => { { let ptr = $ptr; $( let ptr = unsafe { vfb_sys::ffi::$fnc(ptr) }; )* ptr } }; } macro_rules! impl_video_frame_buffer { ($x:ty $(, $cast:ident)*) => { // Allow unused_unsafe when we don't do any cast ( e.g. NativeBuffer ) #[allow(unused_unsafe)] impl VideoFrameBufferTrait for $x { fn width(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).width() } } fn height(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).height() } } // Require ownership because libwebrtc uses the same pointers fn to_i420(self) -> I420Buffer { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*) as *const vfb_sys::ffi::VideoFrameBuffer as *mut vfb_sys::ffi::VideoFrameBuffer; unsafe { I420Buffer::new(Pin::new_unchecked(&mut *ptr).to_i420()) } } } }; } macro_rules! impl_yuv_buffer { ($x:ty $(, $cast:ident)*) => { impl PlanarYuvBuffer for $x { fn chroma_width(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).chroma_width() } } fn chroma_height(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).chroma_height() } } fn stride_y(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).stride_y() } } fn stride_u(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).stride_u() } } fn stride_v(&self) -> i32 { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { (*ptr).stride_v() } } } }; } macro_rules! impl_yuv8_buffer { ($x:ty $(, $cast:ident)*) => { impl PlanarYuv8Buffer for $x { fn data_y(&self) -> &[u8] { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { slice::from_raw_parts((*ptr).data_y(), (self.width() * self.height()) as usize) } } fn data_u(&self) -> &[u8] { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { let chroma_height = (self.height() + 1) / 2; slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize) } } fn data_v(&self) -> &[u8] { let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*); unsafe { let chroma_height = (self.height() + 1) / 2; slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize) } } } }; } pub struct NativeBuffer { cxx_handle: UniquePtr, } pub struct I420Buffer { cxx_handle: UniquePtr, } pub struct I420ABuffer { cxx_handle: UniquePtr, } pub struct I422Buffer { cxx_handle: UniquePtr, } pub struct I444Buffer { cxx_handle: UniquePtr, } pub struct I010Buffer { cxx_handle: UniquePtr, } pub struct NV12Buffer { cxx_handle: UniquePtr, } impl_video_frame_buffer!(NativeBuffer); impl_video_frame_buffer!(I420Buffer, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb); impl_video_frame_buffer!(I420ABuffer); impl_video_frame_buffer!(I422Buffer); impl_video_frame_buffer!(I444Buffer); impl_video_frame_buffer!(I010Buffer); impl_video_frame_buffer!(NV12Buffer); impl_yuv_buffer!(I420Buffer, i420_to_yuv8, yuv8_to_yuv); impl_yuv8_buffer!(I420Buffer, i420_to_yuv8); impl NativeBuffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } } impl I420Buffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } } impl I420ABuffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } } impl I422Buffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } } impl I444Buffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } } impl I010Buffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } } impl NV12Buffer { fn new(cxx_handle: UniquePtr) -> Self { Self { cxx_handle } } }