* Create README.md * add example * crates & examples * fix syntax * add LICENSE * thirdparty LICENSE * rearrange repo * fix build * egui versions * prepare publish * fix demo compilation * add test ci * Update rust.yml * forgot runs-on * install protoc before building * avoid rate limit * include submodules * updates to readme * cache rust builds Co-authored-by: David Zhao <[email protected]> Co-authored-by: David Zhao <[email protected]>
263 lines
7.4 KiB
Rust
263 lines
7.4 KiB
Rust
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<vfb_sys::ffi::VideoFrameBuffer>) -> 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<vfb_sys::ffi::VideoFrameBuffer>,
|
|
}
|
|
|
|
pub struct I420Buffer {
|
|
cxx_handle: UniquePtr<vfb_sys::ffi::I420Buffer>,
|
|
}
|
|
|
|
pub struct I420ABuffer {
|
|
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
|
}
|
|
|
|
pub struct I422Buffer {
|
|
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
|
}
|
|
|
|
pub struct I444Buffer {
|
|
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
|
}
|
|
|
|
pub struct I010Buffer {
|
|
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
|
}
|
|
|
|
pub struct NV12Buffer {
|
|
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
|
}
|
|
|
|
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<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|
|
|
|
impl I420Buffer {
|
|
fn new(cxx_handle: UniquePtr<vfb_sys::ffi::I420Buffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|
|
|
|
impl I420ABuffer {
|
|
fn new(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|
|
|
|
impl I422Buffer {
|
|
fn new(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|
|
|
|
impl I444Buffer {
|
|
fn new(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|
|
|
|
impl I010Buffer {
|
|
fn new(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|
|
|
|
impl NV12Buffer {
|
|
fn new(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
|
Self { cxx_handle }
|
|
}
|
|
}
|